diff --git a/example-dimo-auth/dimo-network-login-with-dimo-0.0.10.tgz b/example-dimo-auth/dimo-network-login-with-dimo-0.0.10.tgz new file mode 100644 index 0000000..dc2fbaf Binary files /dev/null and b/example-dimo-auth/dimo-network-login-with-dimo-0.0.10.tgz differ diff --git a/example-dimo-auth/dimo-network-login-with-dimo-0.0.9.tgz b/example-dimo-auth/dimo-network-login-with-dimo-0.0.9.tgz index 33d1c0d..f0a2e91 100644 Binary files a/example-dimo-auth/dimo-network-login-with-dimo-0.0.9.tgz and b/example-dimo-auth/dimo-network-login-with-dimo-0.0.9.tgz differ diff --git a/example-dimo-auth/package-lock.json b/example-dimo-auth/package-lock.json index 4de133b..c7ff143 100644 --- a/example-dimo-auth/package-lock.json +++ b/example-dimo-auth/package-lock.json @@ -8,7 +8,7 @@ "name": "example-dimo-auth", "version": "0.1.0", "dependencies": { - "@dimo-network/login-with-dimo": "file:./dimo-network-login-with-dimo-0.0.9.tgz", + "@dimo-network/login-with-dimo": "file:./dimo-network-login-with-dimo-0.0.10.tgz", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -2235,9 +2235,9 @@ } }, "node_modules/@dimo-network/login-with-dimo": { - "version": "0.0.9", - "resolved": "file:dimo-network-login-with-dimo-0.0.9.tgz", - "integrity": "sha512-F2xCQOcB+QkMIN/4gbSr88m9+k1kGfF6lBaDUjxcdAqyYMO0lCHVRBLwAl8J5e8jA4aIWHDerDhX+JWu6Roc1Q==", + "version": "0.0.10", + "resolved": "file:dimo-network-login-with-dimo-0.0.10.tgz", + "integrity": "sha512-GKPGZ2yI2nTzR26Ghwxa+EnnbLWf4djKyUggMkNCkQjhh3sq/YrJNwvgh4F4eBnAGP5Mj+PhDr9n1V3Ddhi8aA==", "license": "ISC", "peerDependencies": { "react": "^18.0.0", @@ -3582,9 +3582,9 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/node": { - "version": "16.18.120", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.120.tgz", - "integrity": "sha512-Dmi4bhZ7CHyD4sv4awCZx9RBxWOXSejxTF6B5WQ5UzfLcyEg7JqdDDsjvdMRYES9EcTWHlHZe01PInSj18yP2A==" + "version": "16.18.121", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.121.tgz", + "integrity": "sha512-Gk/pOy8H0cvX8qNrwzElYIECpcUn87w4EAEFXFvPJ8qsP9QR/YqukUORSy0zmyDyvdo149idPpy4W6iC5aSbQA==" }, "node_modules/@types/node-forge": { "version": "1.3.11", @@ -6374,9 +6374,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.65", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.65.tgz", - "integrity": "sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==" + "version": "1.5.67", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz", + "integrity": "sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==" }, "node_modules/emittery": { "version": "0.8.1", @@ -8079,11 +8079,14 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.1.0.tgz", + "integrity": "sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA==", "dependencies": { - "get-intrinsic": "^1.1.3" + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8901,12 +8904,14 @@ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.0.tgz", + "integrity": "sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "gopd": "^1.1.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -10768,9 +10773,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.13", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", - "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==" + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==" }, "node_modules/object-assign": { "version": "4.1.1", @@ -12618,9 +12623,9 @@ } }, "node_modules/psl": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.13.0.tgz", - "integrity": "sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.14.0.tgz", + "integrity": "sha512-Syk1bnf6fRZ9wQs03AtKJHcM12cKbOLo9L8JtCCdYj5/DTsHmTyXM4BK5ouWeG2P6kZ4nmFvuNTdtaqfobCOCg==", "dependencies": { "punycode": "^2.3.1" } @@ -13761,9 +13766,12 @@ } }, "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -15674,9 +15682,9 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", + "integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", diff --git a/example-dimo-auth/package.json b/example-dimo-auth/package.json index d5f9e57..2469ab7 100644 --- a/example-dimo-auth/package.json +++ b/example-dimo-auth/package.json @@ -10,7 +10,7 @@ "@types/node": "^16.18.114", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.1", - "@dimo-network/login-with-dimo": "file:./dimo-network-login-with-dimo-0.0.9.tgz", + "@dimo-network/login-with-dimo": "file:./dimo-network-login-with-dimo-0.0.10.tgz", "react": "^18.3.1", "react-dom": "^18.3.1", "react-scripts": "5.0.1", diff --git a/example-dimo-auth/src/App.tsx b/example-dimo-auth/src/App.tsx index c09f1fc..31d96c7 100644 --- a/example-dimo-auth/src/App.tsx +++ b/example-dimo-auth/src/App.tsx @@ -11,7 +11,8 @@ import { function App() { const [permissionsEnabled, setPermissionsEnabled] = useState(false); - const { isAuthenticated, getValidJWT } = useDimoAuthState(); + const { isAuthenticated, getValidJWT, email, getEmail, walletAddress } = + useDimoAuthState(); const sampleAbi = [ { @@ -1120,6 +1121,14 @@ function App() { + {isAuthenticated && ( +
+

Connected User

+

Wallet Address:{walletAddress}

+ {email &&

{email}

} +
+ )} +

Popup Example

diff --git a/sdk/package-lock.json b/sdk/package-lock.json index 8bb2c8a..ec8e209 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dimo-network/login-with-dimo", - "version": "0.0.8", + "version": "0.0.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dimo-network/login-with-dimo", - "version": "0.0.8", + "version": "0.0.9", "license": "ISC", "devDependencies": { "@types/react": "^18.3.11", @@ -122,12 +122,12 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz", - "integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==", + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", "dev": true, "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/@types/prop-types": { @@ -443,9 +443,9 @@ "dev": true }, "node_modules/caniuse-lite": { - "version": "1.0.30001683", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001683.tgz", - "integrity": "sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==", + "version": "1.0.30001684", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", + "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", "dev": true, "funding": [ { @@ -599,9 +599,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.64", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz", - "integrity": "sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ==", + "version": "1.5.65", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.65.tgz", + "integrity": "sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==", "dev": true }, "node_modules/enhanced-resolve": { @@ -1014,9 +1014,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -1604,9 +1604,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -1617,9 +1617,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true }, "node_modules/update-browserslist-db": { diff --git a/sdk/package.json b/sdk/package.json index bc1dbff..e55cea4 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@dimo-network/login-with-dimo", - "version": "0.0.9", + "version": "0.0.10", "description": "", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/sdk/src/auth/context/DimoAuthContext.tsx b/sdk/src/auth/context/DimoAuthContext.tsx index c020e48..8418dba 100644 --- a/sdk/src/auth/context/DimoAuthContext.tsx +++ b/sdk/src/auth/context/DimoAuthContext.tsx @@ -1,12 +1,15 @@ // DimoAuthContext.tsx import React, { createContext, useContext, useEffect, useState } from "react"; -import { getJWTFromCookies } from "../../storage/storageManager"; +import { getEmailFromLocalStorage, getJWTFromCookies, getWalletAddressFromLocalStorage } from "../../storage/storageManager"; import { isTokenExpired } from "../../token/tokenManager"; // Define the type of the context type DimoAuthContextType = { isAuthenticated: boolean; // Read-only for app developers + walletAddress: string | null; + email: string | null; getValidJWT: () => string | null; + getEmail: () => string | null; }; // Create the context @@ -29,6 +32,18 @@ export const DimoAuthProvider = ({ children: React.ReactNode; }) => { const [isAuthenticated, setAuthenticated] = useState(false); + + const hasEmailPermission = !!getEmailFromLocalStorage(); + const email = hasEmailPermission ? getEmailFromLocalStorage() : ""; + const walletAddress = getWalletAddressFromLocalStorage(); + + const getEmail = () => { + if (hasEmailPermission) { + return email; + } else { + throw new Error("No permission to access email"); + } + }; const getValidJWT = () => { @@ -47,7 +62,7 @@ export const DimoAuthProvider = ({ }, []); return ( - + {children} diff --git a/sdk/src/auth/embedAuth.ts b/sdk/src/auth/embedAuth.ts index be49361..c94840f 100644 --- a/sdk/src/auth/embedAuth.ts +++ b/sdk/src/auth/embedAuth.ts @@ -1,37 +1,11 @@ import { EntryState } from "../enums/globalEnums"; +import { BasePayload } from "../types/BasePayload"; import { TransactionData } from "../types/TransactionData"; import { handleMessageForEmbed } from "../utils/eventHandler"; export const embedAuth = ( - entryState: EntryState, - onSuccess: (data: { - token: string; - transactionHash?: string; - transactionReceipt?: any; - }) => void, - onError: (error: Error) => void, - setAuthenticated: React.Dispatch>, - dimoLogin: string, - clientId?: string, - redirectUri?: string, - apiKey?: string, - permissionTemplateId?: string, - vehicles?: string[], - vehicleMakes?: string[], - transactionData?: TransactionData + basePayload: BasePayload, + data?: Record ) => { - const cleanup = handleMessageForEmbed( - dimoLogin, - entryState, - onSuccess, - onError, - setAuthenticated, - clientId, - redirectUri, - apiKey, - permissionTemplateId, - vehicles, - vehicleMakes, - transactionData - ); + const cleanup = handleMessageForEmbed(basePayload, data); }; diff --git a/sdk/src/auth/popupAuth.ts b/sdk/src/auth/popupAuth.ts index b79263e..9dc47a1 100644 --- a/sdk/src/auth/popupAuth.ts +++ b/sdk/src/auth/popupAuth.ts @@ -1,26 +1,14 @@ import { EntryState } from "../enums/globalEnums"; +import { BasePayload } from "../types/BasePayload"; import { TransactionData } from "../types/TransactionData"; import { handleMessageForPopup } from "../utils/eventHandler"; export const popupAuth = ( - entryState: EntryState, - onSuccess: (data: { - token: string; - transactionHash?: string; - transactionReceipt?: any; - }) => void, - onError: (error: Error) => void, - setAuthenticated: React.Dispatch>, - dimoLogin: string, - clientId?: string, - redirectUri?: string, - apiKey?: string, - permissionTemplateId?: string, - vehicles?: string[], - vehicleMakes?: string[], - transactionData?: TransactionData + basePayload: BasePayload, + data?: Record // Component-specific data ) => { try { + const { entryState, onSuccess, onError, setAuthenticated, dimoLogin } = basePayload; const popup = window.open( dimoLogin, "_blank", @@ -32,26 +20,12 @@ export const popupAuth = ( } // Set up message handler for popup auth - const cleanup = handleMessageForPopup( - dimoLogin, - entryState, - onSuccess, - onError, - setAuthenticated, - popup, - clientId, - redirectUri, - apiKey, - permissionTemplateId, - vehicles, - vehicleMakes, - transactionData - ); + const cleanup = handleMessageForPopup(basePayload, data, dimoLogin, popup); } catch (error: unknown) { if (error instanceof Error) { - onError(error); + basePayload.onError(error); } else { - onError(new Error("An unknown error occurred")); + basePayload.onError(new Error("An unknown error occurred")); } } }; diff --git a/sdk/src/auth/redirectAuth.ts b/sdk/src/auth/redirectAuth.ts index 79e7505..0297393 100644 --- a/sdk/src/auth/redirectAuth.ts +++ b/sdk/src/auth/redirectAuth.ts @@ -1,33 +1,29 @@ import { EntryState } from "../enums/globalEnums"; +import { BasePayload } from "../types/BasePayload"; import { TransactionData } from "../types/TransactionData"; -export const redirectAuth = ( - entryState: EntryState, - onSuccess: (authData: { token: string }) => void, - onError: (error: Error) => void, - dimoLogin: string, - clientId?: string, - redirectUri?: string, - apiKey?: string, //We don't want to send an API key in the url - permissionTemplateId?: string, - vehicles?: string[], - vehicleMakes?: string[], - transactionData?: TransactionData // Add transactionData as a parameter -) => { +export const redirectAuth = (payload: BasePayload, data?: any) => { // Create URLSearchParams instance + + const { clientId, redirectUri, entryState, dimoLogin } = payload; + + const { permissionTemplateId, vehicles, vehicleMakes, transactionData } = data; + const params = new URLSearchParams(); + + if (clientId) params.append("clientId", clientId); if (redirectUri) params.append("redirectUri", redirectUri); if (permissionTemplateId) params.append("permissionTemplateId", permissionTemplateId); if (entryState) params.append("entryState", entryState); if (vehicles && vehicles.length > 0) { - vehicles.forEach((vehicle) => params.append("vehicles", vehicle)); + vehicles.forEach((vehicle: string) => params.append("vehicles", vehicle)); } if (vehicleMakes && vehicleMakes.length > 0) { - vehicleMakes.forEach((vehicleMake) => + vehicleMakes.forEach((vehicleMake: string) => params.append("vehicleMakes", vehicleMake) ); } diff --git a/sdk/src/components/BaseDimoButton.tsx b/sdk/src/components/BaseDimoButton.tsx index f3ec73a..d787140 100644 --- a/sdk/src/components/BaseDimoButton.tsx +++ b/sdk/src/components/BaseDimoButton.tsx @@ -10,7 +10,6 @@ import { useDimoAuthState, useDimoAuthUpdater, } from "../auth/context/DimoAuthContext"; -import { TransactionData } from "../types/TransactionData"; interface BaseDimoButtonProps { mode: "popup" | "embed" | "redirect"; @@ -23,10 +22,7 @@ interface BaseDimoButtonProps { onError: (error: Error) => void; // Error callback buttonLabel: (authenticated: boolean) => string; // Function to determine button label dynamically disableIfAuthenticated?: boolean; // Disable button if authenticated (default: false) - permissionTemplateId?: string; - vehicles?: string[]; - vehicleMakes?: string[]; - transactionData?: TransactionData; + payload: Record; // Dynamic payload object } const BaseDimoButton: React.FC = ({ @@ -36,10 +32,7 @@ const BaseDimoButton: React.FC = ({ onError, buttonLabel, disableIfAuthenticated = false, - permissionTemplateId, - vehicles, - vehicleMakes, - transactionData, + payload, }) => { const { clientId, redirectUri, apiKey, environment } = getDimoConfig(); @@ -52,38 +45,24 @@ const BaseDimoButton: React.FC = ({ ? "https://login.dev.dimo.org" : "https://login.dimo.org"; + const basePayload = { + entryState, + onSuccess, + onError, + setAuthenticated, + dimoLogin, + clientId, + redirectUri, + apiKey, + }; + const handleButtonClick = () => { switch (mode) { case "popup": - popupAuth( - entryState, - onSuccess, - onError, - setAuthenticated, - dimoLogin, - clientId, - redirectUri, - apiKey, - permissionTemplateId, - vehicles, - vehicleMakes, - transactionData - ); + popupAuth(basePayload, payload); break; case "redirect": - redirectAuth( - entryState, - onSuccess, - onError, - dimoLogin, - clientId, - redirectUri, - apiKey, - permissionTemplateId, - vehicles, - vehicleMakes, - transactionData - ); + redirectAuth(basePayload, payload); break; default: console.error("Unsupported mode for button click"); @@ -93,20 +72,7 @@ const BaseDimoButton: React.FC = ({ // Trigger embedAuth only once the iframe has fully loaded const handleIframeLoad = () => { if (mode === "embed") { - embedAuth( - entryState, - onSuccess, - onError, - setAuthenticated, - dimoLogin, - clientId, - redirectUri, - apiKey, - permissionTemplateId, - vehicles, - vehicleMakes, - transactionData - ); + embedAuth(basePayload, payload); } }; diff --git a/sdk/src/components/ExecuteAdvancedTransactionWithDimo.tsx b/sdk/src/components/ExecuteAdvancedTransactionWithDimo.tsx index f6c9f9c..3fed1ba 100644 --- a/sdk/src/components/ExecuteAdvancedTransactionWithDimo.tsx +++ b/sdk/src/components/ExecuteAdvancedTransactionWithDimo.tsx @@ -39,7 +39,7 @@ const ExecuteAdvancedTransactionWithDimo: React.FC< onSuccess={onSuccess} onError={onError} buttonLabel={() => "Execute Advanced Transaction with Dimo"} - transactionData={transactionData} + payload={{ transactionData, eventType:"EXECUTE_ADVANCED_TRANSACTION"}} /> ); }; diff --git a/sdk/src/components/LoginWithDimo.tsx b/sdk/src/components/LoginWithDimo.tsx index 1a0ab28..78a4b64 100644 --- a/sdk/src/components/LoginWithDimo.tsx +++ b/sdk/src/components/LoginWithDimo.tsx @@ -29,9 +29,7 @@ const LoginWithDimo: React.FC = ({ authenticated ? "Connected with DIMO" : "Continue with DIMO" } // Dynamic label based on auth state disableIfAuthenticated={true} // Disable button when authenticated - permissionTemplateId={permissionTemplateId} // Pass permissionTemplateId if provided - vehicles={vehicles} // Pass vehicles if provided - vehicleMakes={vehicleMakes} + payload={{ permissionTemplateId, vehicles, vehicleMakes, eventType: "SHARE_VEHICLES_DATA" }} /> ); }; diff --git a/sdk/src/components/ShareVehiclesWithDimo.tsx b/sdk/src/components/ShareVehiclesWithDimo.tsx index 9445a99..7946dd5 100644 --- a/sdk/src/components/ShareVehiclesWithDimo.tsx +++ b/sdk/src/components/ShareVehiclesWithDimo.tsx @@ -17,7 +17,7 @@ const ShareVehiclesWithDimo: React.FC = ({ onError, permissionTemplateId, vehicles, - vehicleMakes + vehicleMakes, }) => { return ( = ({ onSuccess={onSuccess} onError={onError} buttonLabel={() => "Share Vehicles with DIMO"} - permissionTemplateId={permissionTemplateId} // Pass permissions template - vehicles={vehicles} // Pass vehicle IDs - vehicleMakes={vehicleMakes} + payload={{ permissionTemplateId, vehicles, vehicleMakes, eventType: "SHARE_VEHICLES_DATA" }} /> ); }; diff --git a/sdk/src/storage/storageManager.ts b/sdk/src/storage/storageManager.ts index 585987f..ec1fb4f 100644 --- a/sdk/src/storage/storageManager.ts +++ b/sdk/src/storage/storageManager.ts @@ -9,13 +9,36 @@ */ export const storeJWTInCookies = (jwt: string): void => { - const expirationDate = new Date(); - expirationDate.setFullYear(expirationDate.getFullYear() + 10); // Set expiration to 10 years in the future - - document.cookie = `dimo_auth_token=${jwt}; expires=${expirationDate.toUTCString()}; path=/`; + const expirationDate = new Date(); + expirationDate.setFullYear(expirationDate.getFullYear() + 10); // Set expiration to 10 years in the future + + document.cookie = `dimo_auth_token=${jwt}; expires=${expirationDate.toUTCString()}; path=/`; +}; + +// Utility function to store user properties in localStorage for a given clientId +export const storeWalletAddressInLocalStorage = ( + walletAddress: string +): void => { + localStorage.setItem(`dimo_wallet_address`, walletAddress); +}; + +export const storeEmailInLocalStorage = (email: string): void => { + localStorage.setItem(`dimo_user_email`, email); }; export const getJWTFromCookies = (): string | null => { - const cookie = document.cookie.split('; ').find(row => row.startsWith(`dimo_auth_token=`)); - return cookie ? cookie.split('=')[1] : null; + const cookie = document.cookie + .split("; ") + .find((row) => row.startsWith(`dimo_auth_token=`)); + return cookie ? cookie.split("=")[1] : null; +}; + +export const getWalletAddressFromLocalStorage = (): string | null => { + const walletAddress = localStorage.getItem("dimo_wallet_address"); + return walletAddress; +}; + +export const getEmailFromLocalStorage = (): string | null => { + const email = localStorage.getItem("dimo_user_email"); + return email; }; diff --git a/sdk/src/types/BasePayload.ts b/sdk/src/types/BasePayload.ts new file mode 100644 index 0000000..ac1c816 --- /dev/null +++ b/sdk/src/types/BasePayload.ts @@ -0,0 +1,16 @@ +import { EntryState } from "../enums/globalEnums"; + +export interface BasePayload { + entryState: EntryState; + onSuccess: (data: { + token: string; + transactionHash?: string; + transactionReceipt?: any; + }) => void; + onError: (error: Error) => void; + setAuthenticated: React.Dispatch>; + dimoLogin: string; + clientId?: string; + redirectUri?: string; + apiKey?: string; // Avoid sending API key in the URL +} diff --git a/sdk/src/utils/eventHandler.ts b/sdk/src/utils/eventHandler.ts index 01e1a6b..770ea52 100644 --- a/sdk/src/utils/eventHandler.ts +++ b/sdk/src/utils/eventHandler.ts @@ -1,5 +1,10 @@ import { EntryState } from "../enums/globalEnums"; -import { storeJWTInCookies } from "../storage/storageManager"; +import { + storeEmailInLocalStorage, + storeJWTInCookies, + storeWalletAddressInLocalStorage, +} from "../storage/storageManager"; +import { BasePayload } from "../types/BasePayload"; import { TransactionData } from "../types/TransactionData"; /** @@ -19,163 +24,185 @@ function getDomain(url: string) { return parsedUrl.hostname; } -export const handleMessageForPopup = ( - expectedOrigin: string, - entryState: EntryState, +const validateOrigin = (origin: string, expectedDomain: string): boolean => { + if (getDomain(origin) !== getDomain(expectedDomain)) { + console.warn("Received message from an unknown origin:", origin); + return false; + } + return true; +}; + +const sendMessageToTarget = ( + target: Window | null | undefined, + message: object, + origin: string, + onError: (error: Error) => void +) => { + if (target) { + setTimeout(() => { + target.postMessage(message, origin); + }, 0); + } else { + onError(new Error("Target window not available to send credentials")); + } +}; + +const processAuthResponse = ( + { token, walletAddress, email }: any, + setAuthenticated: (status: boolean) => void, onSuccess: (data: { token: string; transactionHash?: string; transactionReceipt?: any; - }) => void, - onError: (error: Error) => void, - setAuthenticated: React.Dispatch>, - popup?: Window | null, - clientId?: string, - redirectUri?: string, - apiKey?: string, - permissionTemplateId?: string, - vehicles?: string[], - vehicleMakes?: string[], - transactionData?: TransactionData + }) => void ) => { - const popupListener = (event: MessageEvent) => { - if (getDomain(event.origin) !== getDomain(expectedOrigin)) { - console.warn("Received message from an unknown origin:", event.origin); - return; - } + if (walletAddress) storeWalletAddressInLocalStorage(walletAddress); + if (email) storeEmailInLocalStorage(email); + if (token) { + storeJWTInCookies(token); + setAuthenticated(true); + onSuccess({ token }); + } +}; - const { eventType, token, authType, transactionHash, message } = event.data; - - // Handle the "READY" message - if (eventType === "READY") { - // Once the "READY" message is received, send the credentials - if (popup) { - //Temporary Fix - //Seems like on Safari, and Mobile Browsers - the popup is not ready to receive messages, even after sending a "READY" message - //The set timeout acts as a solution, by modifying the callback loop - setTimeout(() => { - popup.postMessage( - { - clientId, - redirectUri, - apiKey, - permissionTemplateId, - vehicles, - vehicleMakes, - transactionData, - entryState, - eventType: "AUTH_INIT", - }, - expectedOrigin - ); - }, 0); - } else { - onError(new Error("Popup window not available to send credentials")); - } - } +// Popup Handler +export const handleMessageForPopup = ( + basePayload: BasePayload, + data: any, + expectedOrigin: string, + popup: Window | null +) => { + const { + entryState, + onSuccess, + onError, + setAuthenticated, + clientId, + redirectUri, + apiKey, + } = basePayload; - if (authType === "popup" && token) { - //TBD: Can store user object in DIMO State here, but then would need to handle clearing it out - storeJWTInCookies(token); - setAuthenticated(true); - onSuccess({ token }); + const popupListener = (event: MessageEvent) => { + if (!validateOrigin(event.origin, expectedOrigin)) return; + + const { + eventType, + token, + walletAddress, + email, + mode, + transactionHash, + message, + } = event.data; + + if (mode === "popup") { + if (eventType === "READY") { + const initialMessage = { + clientId, + redirectUri, + apiKey, + entryState, + eventType: "AUTH_INIT", + }; + sendMessageToTarget(popup, initialMessage, expectedOrigin, onError); + } - // Close the popup after success - if (popup && !popup.closed) { - popup.close(); - console.log("Popup closed successfully."); + if (eventType === data.eventType) { + const dataMessage = { ...data, eventType: data.eventType }; + sendMessageToTarget(popup, dataMessage, expectedOrigin, onError); } - window.removeEventListener("message", popupListener); - } + if (eventType === "authResponse") { + processAuthResponse( + { token, walletAddress, email }, + setAuthenticated, + onSuccess + ); + if (popup && !popup.closed) popup.close(); + } - if (eventType === "transactionResponse") { - if (transactionHash) { + if (eventType === "transactionResponse" && transactionHash) { onSuccess({ token: "", transactionHash }); } - } - if (eventType === "DIMO_ERROR") { - onError(message); + if (eventType === "DIMO_ERROR") { + onError(new Error(message)); + } } }; - // Add event listener specifically for popup auth window.addEventListener("message", popupListener); - - // Return a cleanup function to remove this listener return () => window.removeEventListener("message", popupListener); }; -export const handleMessageForEmbed = ( - expectedOrigin: string, - entryState: EntryState, - onSuccess: (data: { - token: string; - transactionHash?: string; - transactionReceipt?: any; - }) => void, - onError: (error: Error) => void, - setAuthenticated: React.Dispatch>, - clientId?: string, - redirectUri?: string, - apiKey?: string, - permissionTemplateId?: string, - vehicles?: string[], - vehicleMakes?: string[], - transactionData?: TransactionData -) => { +// Embed Handler +export const handleMessageForEmbed = (basePayload: BasePayload, data: any) => { + const { + entryState, + onSuccess, + onError, + setAuthenticated, + clientId, + redirectUri, + apiKey, + dimoLogin, + } = basePayload; + const embedListener = (event: MessageEvent) => { - if (getDomain(event.origin) !== getDomain(expectedOrigin)) { - console.warn("Received message from an unknown origin:", event.origin); - return; - } + if (!validateOrigin(event.origin, dimoLogin)) return; + + const iframe = document.getElementById("dimo-iframe"); + + const { + eventType, + token, + walletAddress, + email, + mode, + transactionHash, + transactionReceipt, + message, + } = event.data; + + if (mode === "embed") { + if (eventType === "READY") { + const initialMessage = { + clientId, + redirectUri, + apiKey, + entryState, + eventType: "AUTH_INIT", + }; + //@ts-ignore + sendMessageToTarget(iframe?.contentWindow, initialMessage, dimoLogin, onError); + } - const { eventType, token, authType, transactionHash, transactionReceipt } = - event.data; - - if (eventType === "READY") { - // Once the "READY" message is received, send the credentials - console.log("Ready Message received"); - const iframe = document.getElementById("dimo-iframe"); - - // Define the message data - const message = { - clientId, - apiKey, - redirectUri, - permissionTemplateId, - vehicles, - vehicleMakes, - entryState, - transactionData, - eventType: "AUTH_INIT", - }; - - // Send the message to the iframe - // Replace "https://example-iframe.com" with the actual origin of the iframe's URL - //@ts-ignore - iframe.contentWindow.postMessage(message, expectedOrigin); - } + if (eventType === data.eventType) { + const dataMessage = { ...data, eventType: data.eventType }; + //@ts-ignore + sendMessageToTarget(iframe?.contentWindow, dataMessage, dimoLogin, onError); + } - if (authType === "embed" && token) { - storeJWTInCookies(token); - setAuthenticated(true); - onSuccess({ token }); - } + processAuthResponse( + { token, walletAddress, email }, + setAuthenticated, + onSuccess + ); + + if (eventType === "transactionResponse") { + if (transactionHash || transactionReceipt) { + onSuccess({ token: "", transactionHash, transactionReceipt }); + } else { + onError(new Error("Could not execute transaction")); + } + } - if (eventType === "transactionResponse") { - if (transactionHash || transactionReceipt) { - onSuccess({ token: "", transactionHash, transactionReceipt }); - } else { - onError(Error("Could not execute transaction")); + if (eventType === "DIMO_ERROR") { + onError(new Error(message)); } } }; - // Add event listener specifically for embed auth window.addEventListener("message", embedListener); - - // Return a cleanup function to remove this listener return () => window.removeEventListener("message", embedListener); -}; +}; \ No newline at end of file