Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Procaptcha token #1263

Merged
merged 24 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
137 changes: 137 additions & 0 deletions .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
name: cypress

on:
pull_request:
branches: [main]
paths:
- 'docker/**'
- 'packages/**'
- 'demos/**'
- 'dev/**'
- 'contracts/**'
- '.github/workflows/tests.yml'
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
GH_TOKEN: ${{ github.token }}
NODE_ENV: test

jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Print contexts
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
ENV_CONTEXT: ${{ toJson(env) }}
VARS_CONTEXT: ${{ toJson(vars) }}
JOB_CONTEXT: ${{ toJson(job) }}
STEPS_CONTEXT: ${{ toJson(steps) }}
RUNNER_CONTEXT: ${{ toJson(runner) }}
SECRETS_CONTEXT: ${{ toJson(secrets) }}
STRATEGY_CONTEXT: ${{ toJson(strategy) }}
MATRIX_CONTEXT: ${{ toJson(matrix) }}
NEEDS_CONTEXT: ${{ toJson(needs) }}
INPUTS_CONTEXT: ${{ toJson(inputs) }}
run: |
echo "******************************"
echo "github:" "$GITHUB_CONTEXT"
echo "******************************"
echo "env:" "$ENV_CONTEXT"
echo "******************************"
echo "vars:" "$VARS_CONTEXT"
echo "******************************"
echo "job:" "$JOB_CONTEXT"
echo "******************************"
echo "steps:" "$STEPS_CONTEXT"
echo "******************************"
echo "runner:" "$RUNNER_CONTEXT"
echo "******************************"
echo "secrets:" "$SECRETS_CONTEXT"
echo "******************************"
echo "strategy:" "$STRATEGY_CONTEXT"
echo "******************************"
echo "matrix:" "$MATRIX_CONTEXT"
echo "******************************"
echo "needs:" "$NEEDS_CONTEXT"
echo "******************************"
echo "inputs:" "$INPUTS_CONTEXT"
echo "******************************"

- uses: actions/checkout@v3

- run: mkdir -p protocol/cargo-cache
- run: mkdir -p protocol/target
- run: mkdir -p node_modules
- run: mkdir -p ~/.cache/Cypress

- name: Restore cache
uses: actions/cache/restore@v3
with:
path: |
protocol/cargo-cache
protocol/target
node_modules
~/.cache/Cypress
# note that restoring a cache in github is a pain. The trailing '-' matches any string after the '-', therefore 'abc-' would match a cache named 'abc-1234' or 'abc-5678', etc.
# the problem is 'abc-' will not match a cache named 'abc'! So if you're using wildcard cache name selectors like this, you need a field that changes as the suffix to become the wildcard
# here we're setting the key to an unused cache key so it falls back to the wildcard selector in `restore-keys`
key: some-unused-cache-key
restore-keys: |
project-cache-${{ runner.os }}-${{ runner.arch }}-

- uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
- run: npm i -g npm@$(cat package.json | jq -r .engines.npm)

- run: npm ci

# build all packages in workspace
- run: npm run build:all

- name: Setup env
run: |
cp demos/client-example-server/env.development demos/client-example-server/.env.test
cp demos/client-example/env.development demos/client-example/.env.test
cp dev/scripts/env.test .env.test
cp dev/scripts/env.test dev/scripts/.env.test
cp dev/scripts/env.test packages/cli/.env.test
cp dev/scripts/env.test packages/procaptcha-bundle/.env.test
echo NODE_ENV: $NODE_ENV

- name: Start the docker images
run: |
docker compose --file ./docker/docker-compose.test.yml up -d
docker container ls
sleep 10s

# deploy dapp + protocol and run setup to register, stake and load a dataset for a provider
- run: NODE_ENV=test npm run deploy_protocol
- run: NODE_ENV=test npm run setup

# Build a test version of the procaptcha bundle and run the cypress tests on it and on the React client-example
# Running bundle:dev instead of bundle:prod means the bundle will be built with selectors that can be used
# by the cypress tests to find the elements they need to interact with
- run: NODE_ENV=test npm -w @prosopo/procaptcha-bundle run bundle:dev

# Needs concurrently to avoid vite hanging forever https://github.com/vitejs/vite/discussions/8745
- name: Install concurrently and cypress
run: npm i concurrently cypress

- name: Run the cypress tests on client-example
run: |
npx concurrently "npm run start:server" "npm run start:provider" "npm run start:demo" "sleep 10s && npm -w @prosopo/cypress-shared run cypress:run:client-example" --success "first" --kill-others

- name: Run the cypress tests on client-bundle-example
run: |
npx concurrently "npm run start:server" "npm run start:provider" "npm run start:bundle" "sleep 10s && npm -w @prosopo/cypress-shared run cypress:run:client-bundle-example" --success "first" --kill-others

- name: Run the cypress tests on client-bundle-example explicit rendering
run: |
npx concurrently "npm run start:server" "npm run start:provider" "npm run start:bundle" "sleep 10s && npm -w @prosopo/cypress-shared run cypress:run:client-bundle-example:explicit" --success "first" --kill-others
33 changes: 0 additions & 33 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,36 +115,3 @@ jobs:
# deploy protocol and run the unit tests
- run: npm run deploy_protocol
- run: npm run test

# restart the docker images to drop any changes made by the unit tests to the contract(s) before running cypress tests
- name: Reset the docker containers
run: |
docker compose --file ./docker/docker-compose.test.yml down
docker compose --file ./docker/docker-compose.test.yml up -d
docker container ls
sleep 10s

# deploy dapp + protocol and run setup to register, stake and load a dataset for a provider
- run: NODE_ENV=test npm run deploy_protocol
- run: NODE_ENV=test npm run setup

# Build a test version of the procaptcha bundle and run the cypress tests on it and on the React client-example
# Running bundle:dev instead of bundle:prod means the bundle will be built with selectors that can be used
# by the cypress tests to find the elements they need to interact with
- run: NODE_ENV=test npm -w @prosopo/procaptcha-bundle run bundle:dev

# Needs concurrently to avoid vite hanging forever https://github.com/vitejs/vite/discussions/8745
- name: Install concurrently and cypress
run: npm i concurrently cypress

- name: Run the cypress tests on client-example
run: |
npx concurrently "npm run start:server" "npm run start:provider" "npm run start:demo" "sleep 10s && npm -w @prosopo/cypress-shared run cypress:run:client-example" --success "first" --kill-others

- name: Run the cypress tests on client-bundle-example
run: |
npx concurrently "npm run start:server" "npm run start:provider" "npm run start:bundle" "sleep 10s && npm -w @prosopo/cypress-shared run cypress:run:client-bundle-example" --success "first" --kill-others

- name: Run the cypress tests on client-bundle-example explicit rendering
run: |
npx concurrently "npm run start:server" "npm run start:provider" "npm run start:bundle" "sleep 10s && npm -w @prosopo/cypress-shared run cypress:run:client-bundle-example:explicit" --success "first" --kill-others
7 changes: 2 additions & 5 deletions demos/client-example-server/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ProsopoEnvError, getLoggerDefault } from '@prosopo/common'
import { ProsopoServer, getServerConfig } from '@prosopo/server'
import { getPairAsync } from '@prosopo/contract'
import { getServerConfig } from '@prosopo/server'
import connectionFactory from './utils/connection.js'
import cors from 'cors'
import dotenv from 'dotenv'
Expand Down Expand Up @@ -82,10 +81,8 @@ async function main() {
const config = getServerConfig()

console.log('config', config)
const pair = await getPairAsync(config.networks[config.defaultNetwork], config.account.secret)
const prosopoServer = new ProsopoServer(config, pair)

app.use(routesFactory(mongoose, prosopoServer, verifyEndpoint, verifyType))
app.use(routesFactory(mongoose, config, verifyEndpoint, verifyType))

app.listen(process.env.PROSOPO_SERVER_PORT)
}
Expand Down
47 changes: 33 additions & 14 deletions demos/client-example-server/src/controllers/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { ApiParams, ProcaptchaOutput, ProcaptchaOutputSchema } from '@prosopo/types'
import { ApiParams, ProcaptchaToken, ProsopoServerConfigOutput } from '@prosopo/types'
import { Connection } from 'mongoose'
import { NextFunction, Request, Response } from 'express'
import { ProcaptchaResponse } from '@prosopo/types'
import { ProsopoEnvError } from '@prosopo/common'
import { ProsopoServer } from '@prosopo/server'
import { UserInterface } from '../models/user.js'
import { at } from '@prosopo/util'
import { blake2b } from '@noble/hashes/blake2b'
import { getPairAsync } from '@prosopo/contract'
import { randomAsHex } from '@polkadot/util-crypto'
import { u8aToHex } from '@polkadot/util'
import { z } from 'zod'
Expand All @@ -39,33 +41,34 @@
prosopoServer: ProsopoServer,
verifyType: string,
verifyEndpoint: string,
data: ProcaptchaOutput
token: ProcaptchaToken,
secret: string
) => {
if (verifyType === 'api') {
// verify using the API endpoint
console.log('verifying using the API endpoint')

const response = await fetch(verifyEndpoint, {
method: 'POST',
body: JSON.stringify(data),
body: JSON.stringify({ [ApiParams.procaptchaResponse]: token, [ApiParams.secret]: secret }),
})
return (await response.json()).verified
} else {
// verify using the TypeScript library
console.log('verifying using the TypeScript library')

return await prosopoServer.isVerified(data)
return await prosopoServer.isVerified(token)
}
}

const signup = async (
mongoose: Connection,
prosopoServer: ProsopoServer,
config: ProsopoServerConfigOutput,
verifyEndpoint: string,
verifyType: string,
req: Request,
res: Response,
next: NextFunction

Check warning on line 71 in demos/client-example-server/src/controllers/auth.ts

View workflow job for this annotation

GitHub Actions / check

'next' is defined but never used
) => {
try {
const User = mongoose.model<UserInterface>('User')
Expand All @@ -74,18 +77,26 @@
email: req.body.email,
})
const payload = SubscribeBodySpec.parse(req.body)
const pair = await getPairAsync(config.networks[config.defaultNetwork], config.account.secret)
const prosopoServer = new ProsopoServer(config, pair)
await prosopoServer.isReady()
if (dbUser) {
return res.status(409).json({ message: 'email already exists' })
}
console.log('payload', payload)
console.log('Request payload', payload)

// get the procaptcha-response token
const token = payload[ApiParams.procaptchaResponse]

// get the contents of the procaptcha-response JSON data
const data = ProcaptchaOutputSchema.parse(payload[ApiParams.procaptchaResponse])
console.log('Sending Procaptcha token', token)

console.log('sending data', data)
if (!config.account.secret) {
throw new ProsopoEnvError('GENERAL.MNEMONIC_UNDEFINED', {
context: { missingParams: ['PROSOPO_SITE_PRIVATE_KEY'] },
})
}

const verified = await verify(prosopoServer, verifyType, verifyEndpoint, data)
const verified = await verify(prosopoServer, verifyType, verifyEndpoint, token, config.account.secret)

if (verified) {
// salt
Expand Down Expand Up @@ -118,13 +129,15 @@

const login = async (
mongoose: Connection,
prosopoServer: ProsopoServer,
config: ProsopoServerConfigOutput,
verifyEndpoint: string,
verifyType: string,
req: Request,
res: Response
) => {
const User = mongoose.model<UserInterface>('User')
const pair = await getPairAsync(config.networks[config.defaultNetwork], config.account.secret)
const prosopoServer = new ProsopoServer(config, pair)
await prosopoServer.isReady()
// checks if email exists
await User.findOne({
Expand All @@ -134,11 +147,17 @@
if (!dbUser) {
res.status(404).json({ message: 'user not found' })
} else {
const payload = SubscribeBodySpec.parse(req.body)
const payload = SubscribeBodySpec.parse(req.body[ApiParams.procaptchaResponse])

const data = ProcaptchaOutputSchema.parse(payload[ApiParams.procaptchaResponse])
const token = payload[ApiParams.procaptchaResponse]

if (!config.account.secret) {
throw new ProsopoEnvError('GENERAL.MNEMONIC_UNDEFINED', {
context: { missingParams: ['PROSOPO_SITE_PRIVATE_KEY'] },
})
}

const verified = await verify(prosopoServer, verifyType, verifyEndpoint, data)
const verified = await verify(prosopoServer, verifyType, verifyEndpoint, token, config.account.secret)

if (verified) {
// password hash
Expand Down
8 changes: 4 additions & 4 deletions demos/client-example-server/src/routes/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Connection } from 'mongoose'
import { ProsopoServer } from '@prosopo/server'
import { ProsopoServerConfigOutput } from '@prosopo/types'
import { isAuth, login, signup } from '../controllers/auth.js'
import express from 'express'

const router = express.Router()

function getRoutes(
mongoose: Connection,
prosopoServer: ProsopoServer,
config: ProsopoServerConfigOutput,
verifyEndpoint: string,
verifyType: string
): express.Router {
router.post('/login', login.bind(null, mongoose, prosopoServer, verifyEndpoint, verifyType))
router.post('/login', login.bind(null, mongoose, config, verifyEndpoint, verifyType))

router.post('/signup', signup.bind(null, mongoose, prosopoServer, verifyEndpoint, verifyType))
router.post('/signup', signup.bind(null, mongoose, config, verifyEndpoint, verifyType))

router.get('/private', isAuth)

Expand Down
2 changes: 1 addition & 1 deletion demos/client-example/env.development
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ PROSOPO_PORT=9230
PROSOPO_DEFAULT_NETWORK=development
PROSOPO_DEFAULT_ENVIRONMENT=development
PROSOPO_MONGO_EVENTS_URI=mongodb+srv://<MONGO_URI_HERE>/frictionless_events
_DEV_ONLY_WATCH_EVENTS=false
_DEV_ONLY_WATCH_EVENTS=false
Loading
Loading