Adding the DataVault Connect button to your NextJS react application
For your convenience, you can use the following code to add the DataVault Connect button to your react application or repurpose it for other frameworks as neeeded.
DataVault hook to help construct the authorization url and setup PKCE and state using cookies.
This hook will help you:
- Build the authorization url
- Save the code verifier and state to a cookie
- Return the url and button props
It should be simple to modify it to work with other frameworks, namely by changing the cookie handling library
useDataVaultButton.ts
import { useEffect, useReducer, useState } from 'react'
import { generatePkcePair, generateSecureString } from '@/utils/pkce'
import { setCookie, OptionsType } from 'cookies-next/client'
export type DataVaultAuthorizationParams = {
client_id: string
code_challenge: string
redirect_uri: string
scope: string
state: string
code_challenge_method: string
response_type: string
}
/**
* This hook is used to generate the authorization params for the DataVault button,
* and to set the cookies for state and verifier so they can be used on the callback to
* avoid replay and CSRF attacks.
*
* @param clientId - The client ID for your DataVault app
* @param redirectUri - The redirect URI for your DataVault app
*/
export const useDataVaultButton = (clientId: string, redirectUri: string) => {
const [url, setUrl] = useState('')
const [authorizationParams, updateParams] = useReducer(
(
state: DataVaultAuthorizationParams,
updates: Partial<DataVaultAuthorizationParams>,
) => ({
...state,
...updates,
}),
{
client_id: clientId,
code_challenge: '',
redirect_uri: redirectUri,
scope: 'push_packet',
state: '',
code_challenge_method: 'S256',
response_type: 'code',
},
)
useEffect(() => {
const setupPkce = async () => {
const { codeVerifier, codeChallenge } = await generatePkcePair()
setCookie('code_verifier', codeVerifier, cookieOptions)
const state = generateSecureString(32)
setCookie('state', state, cookieOptions)
updateParams({
code_challenge: codeChallenge,
state,
})
}
setupPkce()
}, [])
useEffect(() => {
setUrl(
new URL(
`/oauth/authorize?${new URLSearchParams(authorizationParams)}`,
'https://source.tartle.co',
).toString(),
)
}, [authorizationParams])
return {
dataVaultAuthorizationParams: authorizationParams,
isReady: authorizationParams.code_challenge !== '',
dataVaultUrl: url,
}
}
const cookieOptions: OptionsType = {
path: '/',
maxAge: 60 * 60, // 1 hour
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
}
For learning how to generate the PKCE pair and state on the browser, you can follow this code
DataVault Connect Button Component
You can use this component to display your DataVault Connect button adhering with our design guidelines.
DataVaultConnectButton.tsx
const DataVaultConnectButton = ({ url }: { url: string }) => (
<a className="tartle-datavault-button" href={url}>
<div className="logo-container">
<svg
viewBox="0 0 35 40"
fill="none"
className="logo-cube"
xmlns="http://www.w3.org/2000/svg"
>
<defs id="defs1" />
<path
d="M 17.162176,39.288721 34.3247,29.6706 34.2922,9.87187 17.1292,0 0,9.9281 0.032495,29.7268 17.162176,39.288721 17.204142,35.299053 3.77803,27.556 3.75303,12.0864 17.1367,4.33042 l 13.41,7.71228 0.025,15.4695 -13.367558,7.786853 z"
fill="currentColor"
id="path1"
/>
<g id="layer1">
<path
d="M 30.5467,12.0427 17.1367,4.33042 3.75303,12.0864 17.16235,19.64436 Z"
fill="currentColor"
id="path1-8"
style={{ fill: '#7c7c7c', fillOpacity: 1 }}
/>
</g>
<g id="layer2">
<path
d="M 17.204142,35.299053 17.16235,19.64436 3.75303,12.0864 3.77803,27.556 Z"
fill="currentColor"
id="path1-8-5"
style={{ fill: '#c8c8c8', fillOpacity: 1 }}
/>
</g>
<g id="layer3">
<path
d="M 17.204142,35.299053 17.149865,19.62251 30.5467,12.0427 l 0.025,15.4695 z"
fill="currentColor"
id="path1-8-5-9"
style={{ fill: '#ffffff', fillOpacity: 1 }}
/>
</g>
</svg>
<svg
className="tartle-logo"
viewBox="0 0 35 40"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17.9966 39.1338L34.3247 29.6706L34.2922 9.87187L17.1292 0L0 9.9281L0.032495 29.7268L13.0075 37.1904L16.8555 39.4037V20.5498H25.8913V16.4419H4.58537V20.5498H13.0075V32.865L3.77803 27.556L3.75303 12.0864L17.1367 4.33042L30.5467 12.0427L30.5717 27.5122L17.9966 34.8008V39.1338Z"
fill="currentColor"
/>
</svg>
</div>
<span>DataVault Connect</span>
</a>
)
export default DataVaultConnectButton
Button css
.tartle-datavault-button {
margin: 0 auto;
display: flex;
width: fit-content;
align-items: center;
justify-content: center;
background-color: hsl(170, 72%, 47%);
color: hsl(180, 100%, 12%);
white-space: nowrap;
font-family: 'Inter', sans-serif;
font-weight: bold;
padding: 0.5rem 1rem 0.5rem 0.5rem;
border-radius: 0.5rem;
font-size: 0.875rem;
transition:
background-color 0.2s ease-in-out,
scale 0.15s ease-in-out;
}
.tartle-datavault-button:hover {
text-decoration: none;
background-color: hsla(170, 72%, 42%);
}
.tartle-datavault-button span {
margin-left: 0.5rem;
user-select: none;
}
.tartle-datavault-button .logo-container {
display: grid;
grid-template-columns: 1fr;
}
.tartle-datavault-button .logo-container svg {
width: 1.5rem;
height: 1.5rem;
color: hsl(180, 100%, 12%);
grid-row-start: 1;
grid-column-start: 1;
}
.tartle-datavault-button:hover .tartle-logo {
opacity: 0;
rotate: 180deg;
animation: pulse 0.3s ease-in-out;
}
.tartle-datavault-button:hover .logo-cube {
opacity: 1;
rotate: 360deg;
animation: pulse 0.3s ease-in-out;
}
.tartle-logo,
.logo-cube {
transition:
opacity 0.3s ease-in-out,
rotate 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86),
scale 0.3s ease-in-out;
}
.tartle-logo {
opacity: 1;
rotate: 0deg;
}
.logo-cube {
opacity: 0;
rotate: 180deg;
}
.tartle-datavault-button:active {
scale: 0.95;
}
.tartle-datavault-button:active .logo-cube {
scale: 0.35;
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(0.7);
}
100% {
transform: scale(1);
}
}
Applying the hook and component to your application
Using both the hook and the component above, you can then add the DataVault Connect button to your application
Adding the DataVault Connect button
'use client'
import DataVaultConnectButton from './DataVaultConnectButton'
import { useDataVaultButton } from './useDataVaultButton'
const Authorization = ({ clientId }: { clientId: string }) => {
const { dataVaultUrl } = useDataVaultButton(clientId, YOUR_REDIRECT_URI)
return (
<div>
{/* the rest of your page */}
<DataVaultConnectButton url={dataVaultUrl} />
</div>
)
}
export default Authorization
For an example of this in action, check out the TARTLE OAuth Test App