Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
29fbddf
add open payments library
lengyel-arpad85 Dec 4, 2024
07183bc
corrected type
lengyel-arpad85 Dec 4, 2024
eb14c77
required grant in api response
lengyel-arpad85 Dec 16, 2024
3dc2aa4
format
lengyel-arpad85 Dec 16, 2024
fb757f2
Merge branch 'main' into al/13-request-grant-confirmation-for-config-…
lengyel-arpad85 Mar 12, 2025
b41355d
OP env vars
lengyel-arpad85 Mar 13, 2025
1186c08
validate form functionality moved to a function
lengyel-arpad85 Mar 13, 2025
423c272
get grant interact url
lengyel-arpad85 Mar 13, 2025
468fd0f
wallet ownership modal and grant confirmation
lengyel-arpad85 Mar 15, 2025
b5f0bbf
Merge branch 'main' into al/13-request-grant-confirmation-for-config-…
lengyel-arpad85 Mar 15, 2025
de8afd3
fix opening grant confirmation link
lengyel-arpad85 Mar 15, 2025
e407768
lint
lengyel-arpad85 Mar 15, 2025
961b383
cleanup OP functions
lengyel-arpad85 Mar 17, 2025
e746c43
add finalizeValidationPayment
lengyel-arpad85 Mar 17, 2025
c289840
Merge branch 'main' into al/13-request-grant-confirmation-for-config-…
lengyel-arpad85 Mar 17, 2025
618e0d3
re-add awaits
lengyel-arpad85 Mar 17, 2025
7a09f69
remove duplicat modal
lengyel-arpad85 Mar 17, 2025
14b72e1
OP functions updated to work as wallet ownership validation
lengyel-arpad85 Mar 18, 2025
3d928d2
process grant response
lengyel-arpad85 Mar 18, 2025
83980cb
backend handle session
lengyel-arpad85 Mar 18, 2025
c08999a
backend cookie handling
lengyel-arpad85 Mar 19, 2025
3a6352b
Merge branch 'main' into al/13-request-grant-confirmation-for-config-…
DarianM Mar 19, 2025
ff89b02
remove query params from form
lengyel-arpad85 Mar 19, 2025
623a8ce
removed fetching quote as unnecesary
lengyel-arpad85 Mar 19, 2025
af10f44
move server files to server directory
lengyel-arpad85 Mar 19, 2025
16ca8a1
moved detection of missing param
lengyel-arpad85 Mar 19, 2025
31f1791
global crypto
lengyel-arpad85 Mar 20, 2025
2c7d59b
painfull this was, state loss is fixed
lengyel-arpad85 Mar 20, 2025
aeb3f94
Update frontend/app/lib/server/open-payments.server.ts
lengyel-arpad85 Mar 20, 2025
c28ec39
revert change
lengyel-arpad85 Mar 20, 2025
874c16a
removed unused code
lengyel-arpad85 Mar 20, 2025
fea654c
removed dangerouslySetInnerHTML
lengyel-arpad85 Mar 20, 2025
865158a
changed order of formdata
lengyel-arpad85 Mar 20, 2025
1803c98
session secret exported
lengyel-arpad85 Mar 21, 2025
1da37fd
fix error on new version creation
lengyel-arpad85 Mar 21, 2025
5d124b0
fixed new version fail when unauthenticated
lengyel-arpad85 Mar 21, 2025
952b57c
Merge branch 'main' into al/13-request-grant-confirmation-for-config-…
lengyel-arpad85 Mar 21, 2025
d584b93
typo fixed
lengyel-arpad85 Mar 21, 2025
31e97f9
standardize wallet address
lengyel-arpad85 Mar 21, 2025
2f00e24
fallback when missing walletaddress
lengyel-arpad85 Mar 21, 2025
987cdfb
ensure new version has value assigned
lengyel-arpad85 Mar 21, 2025
b1e3195
improved logic of when to set new version value
lengyel-arpad85 Mar 21, 2025
d18ae66
changed validation logic order
lengyel-arpad85 Mar 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@
},
"dependencies": {
"@aws-sdk/client-s3": "^3.645.0",
"@remix-run/node": "^2.14.0",
"@smithy/node-http-handler": "^3.2.3",
"@types/express-session": "^1.18.1",
"@types/underscore": "^1.11.15",
"cookie-parser": "^1.4.7",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-session": "^1.18.1",
"he": "^1.2.0",
"module-alias": "^2.2.3",
"sanitize-html": "^2.14.0",
Expand Down
26 changes: 22 additions & 4 deletions backend/src/controllers/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
SanitizedFields,
SaveUserConfigRequest
} from './types.js'
import { getSession } from '@/services/session.js'

export const getDefault = async (_: Request, res: Response) => {
try {
Expand All @@ -33,15 +34,24 @@ export const createUserConfig = async (req: Request, res: Response) => {
const data: CreateConfigRequest = req.body
const tag = data.version || data.tag

if (!data.walletAddress) {
if (!data?.walletAddress) {
throw 'Wallet address is required'
}
const walletAddress = decodeURIComponent(`https://${data.walletAddress}`)

const cookieHeader = req.headers.cookie
const session = await getSession(cookieHeader)

const validForWallet = session?.get('validForWallet')

if (!session || validForWallet !== walletAddress) {
throw 'Grant confirmation is required'
}

const defaultData = await getDefaultData()
const defaultDataContent: ConfigVersions['default'] =
JSON.parse(defaultData).default
defaultDataContent.walletAddress = decodeURIComponent(
`https://${data.walletAddress}`
)
defaultDataContent.walletAddress = walletAddress

sanitizeConfigFields({ ...defaultDataContent, tag })

Expand All @@ -51,6 +61,7 @@ export const createUserConfig = async (req: Request, res: Response) => {
try {
// existing config
const s3data = await s3.send(new GetObjectCommand(params))

// Convert the file stream to a string
fileContentString = await streamToString(
s3data.Body as NodeJS.ReadableStream
Expand Down Expand Up @@ -97,10 +108,17 @@ export const createUserConfig = async (req: Request, res: Response) => {
export const saveUserConfig = async (req: Request, res: Response) => {
try {
const data: SaveUserConfigRequest = req.body
const cookieHeader = req.headers.cookie
const session = await getSession(cookieHeader)

const validForWallet = session?.get('validForWallet')

if (!data.walletAddress) {
throw 'Wallet address is required'
}
if (!session || validForWallet !== data.walletAddress) {
throw 'Grant confirmation is required'
}

const { s3, params } = getS3AndParams(data.walletAddress)

Expand Down
17 changes: 16 additions & 1 deletion backend/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import https from 'https'
import http from 'http'
import fs from 'fs'
import express, { Express } from 'express'
import session from 'express-session'
import routes from './routes/index.js'

const router: Express = express()
Expand All @@ -21,10 +22,24 @@ if (isDevelopment) {
router.use(express.urlencoded({ extended: true }))
router.use(express.json())

// Session middleware
router.use(
session({
secret: process.env.SESSION_COOKIE_SECRET_KEY || 'supersecretilpaystring',
resave: false,
saveUninitialized: true, // Only save the session if it is modified
cookie: {
httpOnly: true,
secure: true,
sameSite: 'none'
}
})
)

router.use((req, res, next) => {
// set the CORS policy
res.header('Access-Control-Allow-Origin', '*')
// set the CORS headers
res.header('Access-Control-Allow-Credentials', 'true')
res.header(
'Access-Control-Allow-Headers',
'origin,X-Requested-With,Content-Type,Accept,Authorization'
Expand Down
16 changes: 16 additions & 0 deletions backend/src/services/session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createCookieSessionStorage } from '@remix-run/node'

const { getSession, commitSession, destroySession } =
createCookieSessionStorage({
cookie: {
name: 'wmtools-session',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think we should add a maxAge or delete the wmtools-session cookie when the session/tab closes?
basically asking the user for a new grant confirmation over a period of time or after the tab closes.

only session gets cleared after a session but not this

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sadly there is no way of detecting when tab closes (browser event onunload fires even when navigating to a different domain, so it would remove the cookie everytime we go to IDP to confirm the grant)
the only solution I see is add maxAge, just need to decide how long that should be ? 10, 30 minutes ? 1h ?

httpOnly: true,
secure: true,
sameSite: 'none',
secrets: [
process.env.SESSION_COOKIE_SECRET_KEY || 'supersecretilpaystring'
]
}
})

export { getSession, commitSession, destroySession }
7 changes: 7 additions & 0 deletions backend/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'express-session'

declare module 'express-session' {
interface Session {
validForWallet?: string
}
}
3 changes: 2 additions & 1 deletion backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
}
},
"include": ["src/**/*", "tests/**/*"],
"exclude": ["node_modules", "**/node_modules/*"]
"exclude": ["node_modules", "**/node_modules/*"],
"files": ["src/types/index.ts"]
}
5 changes: 5 additions & 0 deletions docker/dev/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ SCRIPT_FRONTEND_URL=https://localhost:5100/
SCRIPT_ILPAY_URL=https://interledgerpay.com/extension/
SCRIPT_EMBED_URL=https://localhost:5100/

OP_KEY_ID=
OP_PRIVATE_KEY=
OP_WALLET_ADDRESS=
OP_REDIRECT_URL=

# BACKEND
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
Expand Down
4 changes: 4 additions & 0 deletions docker/dev/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ services:
ILPAY_URL: ${SCRIPT_ILPAY_URL}
FRONTEND_URL: ${SCRIPT_FRONTEND_URL}
INIT_SCRIPT_URL: ${SCRIPT_EMBED_URL}
OP_KEY_ID: ${OP_KEY_ID}
OP_PRIVATE_KEY: ${OP_PRIVATE_KEY}
OP_WALLET_ADDRESS: ${OP_WALLET_ADDRESS}
OP_REDIRECT_URL: ${OP_REDIRECT_URL}
networks:
- wm-tools
command: pnpm run dev
Expand Down
4 changes: 3 additions & 1 deletion frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ RUN pnpm --filter frontend build
ARG VITE_SCRIPT_API_URL
ARG VITE_SCRIPT_FRONTEND_URL
ARG VITE_SCRIPT_ILPAY_URL
ARG VITE_INIT_SCRIPT_URL

ENV VITE_SCRIPT_API_URL=$VITE_SCRIPT_API_URL \
VITE_SCRIPT_FRONTEND_URL=$VITE_SCRIPT_FRONTEND_URL \
VITE_SCRIPT_ILPAY_URL=$VITE_SCRIPT_ILPAY_URL
VITE_SCRIPT_ILPAY_URL=$VITE_SCRIPT_ILPAY_URL \
VITE_INIT_SCRIPT_URL=$VITE_INIT_SCRIPT_URL

RUN pnpm --filter frontend build:init

Expand Down
4 changes: 3 additions & 1 deletion frontend/Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ EXPOSE 5100
ARG VITE_SCRIPT_API_URL
ARG VITE_SCRIPT_FRONTEND_URL
ARG VITE_SCRIPT_ILPAY_URL
ARG VITE_INIT_SCRIPT_URL

ENV VITE_SCRIPT_API_URL=$VITE_SCRIPT_API_URL \
VITE_SCRIPT_FRONTEND_URL=$VITE_SCRIPT_FRONTEND_URL \
VITE_SCRIPT_ILPAY_URL=$VITE_SCRIPT_ILPAY_URL
VITE_SCRIPT_ILPAY_URL=$VITE_SCRIPT_ILPAY_URL \
VITE_INIT_SCRIPT_URL=$VITE_INIT_SCRIPT_URL

WORKDIR /app/frontend

Expand Down
2 changes: 1 addition & 1 deletion frontend/app/components/Snackbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Transition } from '@headlessui/react'
import { cx } from 'class-variance-authority'
import type { FC } from 'react'
import { Fragment, useEffect } from 'react'
import { type Message } from '~/lib/message.server'
import { type Message } from '~/lib/server/message.server.js'
import { CheckCircleSolid, XIcon, XCircleSolid } from '../components/icons.js'

interface SnackbarProps {
Expand Down
10 changes: 8 additions & 2 deletions frontend/app/components/modals/Confirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { Form } from '@remix-run/react'
import { ElementErrors } from '~/lib/types.js'
import { XIcon } from '~/components/icons.js'
import { Button } from '~/components/index.js'
import { ReactElement } from 'react'

type ConfirmModalProps = {
title: string
title: string | ReactElement
description: string | ReactElement
isOpen: boolean
errors?: ElementErrors
onClose: () => void
Expand All @@ -14,6 +16,7 @@ type ConfirmModalProps = {

export const ConfirmModal = ({
title,
description,
isOpen,
onClose,
onConfirm
Expand Down Expand Up @@ -44,13 +47,16 @@ export const ConfirmModal = ({
<div className="mt-2">
<Form method="post" replace preventScrollReset>
<fieldset>
<div className="flex justify-center p-4 mx-2">
{description}
</div>
<div className="flex justify-center space-x-4">
<Button
className="mx-0"
aria-label={`confirm`}
onClick={onConfirm}
>
Yes
Continue
</Button>
<Button aria-label={`close`} onClick={onClose}>
Cancel
Expand Down
22 changes: 17 additions & 5 deletions frontend/app/lib/apiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ export class ApiClient {
const wa = encodeURIComponent(
walletAddress.replace('$', '').replace('https://', '')
)
const response = await axios.get(`${apiUrl}tools/${wa}`, { httpsAgent })
const response = await axios.get(`${apiUrl}tools/${wa}`, {
httpsAgent
})

if (response.status === 200) {
return {
Expand All @@ -73,7 +75,8 @@ export class ApiClient {

public static async createUserConfig(
version: string,
walletAddress: string
walletAddress: string,
cookieHeader: string
): Promise<ApiResponse> {
const tag = encodeURIComponent(version)
const wa = encodeURIComponent(
Expand All @@ -83,7 +86,11 @@ export class ApiClient {
`${apiUrl}tools`,
{ walletAddress: wa, tag },
{
httpsAgent
httpsAgent,
withCredentials: true,
headers: {
Cookie: cookieHeader // Manually attach the session cookie
}
}
)

Expand All @@ -102,10 +109,15 @@ export class ApiClient {
}

public static async saveUserConfig(
configData: Partial<ElementConfigType>
configData: Partial<ElementConfigType>,
cookieHeader: string
): Promise<ApiResponse> {
const response = await axios.put(`${apiUrl}tools`, configData, {
httpsAgent
httpsAgent,
withCredentials: true,
headers: {
Cookie: cookieHeader // Manually attach the session cookie
}
})

if (response.status === 200) {
Expand Down
10 changes: 10 additions & 0 deletions frontend/app/lib/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ import { tooltips } from './tooltips.js'
import { CornerType, SlideAnimationType, PositionType } from './types.js'

export const validConfigTypes = ['button', 'banner', 'widget']
export const modalTypes = [
'confirm',
'import',
'info',
'new-version',
'script',
'wallet-ownership',
'grant-response'
]
export type ModalType = (typeof modalTypes)[number]

export const textColorPresets = ['#ffffff', '#000000']
export const triggerColorPresets = ['#ffffff', '#000000', '#096b63']
Expand Down
Loading