Skip to content

Commit

Permalink
Draft tests, implement email verification (#1)
Browse files Browse the repository at this point in the history
The email verification is stateless (without database), using JSON Web Token.

On successful verification, another JSON Web Token is created, proving that email is verified. It should be saved in person's pod in some discoverable settings.
  • Loading branch information
mrkvon authored Jan 15, 2024
1 parent d9fd231 commit 89753f7
Show file tree
Hide file tree
Showing 15 changed files with 741 additions and 439 deletions.
11 changes: 11 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ BEHIND_PROXY=
MAILER_IDENTITY_EMAIL=bot@example
MAILER_IDENTITY_PASSWORD=password
MAILER_IDENTITY_PROVIDER=http://localhost:3456
MAILER_IDENTITY_WEBID=http://localhost:3456/bot/profile/card#me

# link to group of users who are allowed to use the service
ALLOWED_GROUPS=

# SMTP Transport for sending emails
# string or undefined
Expand All @@ -39,3 +43,10 @@ DB_USERNAME=
DB_PASSWORD=
DB_HOST=
DB_PORT=

# JWT
# path to JWT (private) key
# you can use the command `openssl ecparam -name prime256v1 -genkey -noout -out ecdsa-p256-private.pem` to generate one
JWT_KEY=./ecdsa-p256-private.pem
# jwt algorithm
JWT_ALG=ES256
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ jobs:
- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Generate private key
run: openssl ecparam -name prime256v1 -genkey -noout -out ecdsa-p256-private.pem

- name: Run tests
run: yarn test
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ output
node_modules

database.sqlite

*.pem
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"@types/bcryptjs": "^2.4.2",
"@types/chai": "^4.3.5",
"@types/co-body": "^6.1.0",
"@types/fs-extra": "^11.0.4",
"@types/jsonwebtoken": "^9.0.5",
"@types/koa": "^2.13.7",
"@types/koa-static": "^4.0.3",
"@types/koa__cors": "^4.0.1",
Expand All @@ -37,6 +39,7 @@
"eslint-plugin-prettier": "^5.0.0",
"maildev": "^2.1.0",
"mocha": "^10.2.0",
"msw": "^2.0.14",
"prettier": "^3.0.0",
"rdf-namespaces": "^1.11.0",
"sinon": "^15.2.0",
Expand All @@ -57,6 +60,8 @@
"cross-fetch": "^4.0.0",
"css-authn": "^0.0.14",
"dotenv": "^16.0.0",
"fs-extra": "^11.2.0",
"jsonwebtoken": "^9.0.2",
"koa": "^2.14.2",
"koa-helmet": "^7.0.2",
"koa-static": "^5.0.0",
Expand Down
96 changes: 62 additions & 34 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import Router from '@koa/router'
import Koa from 'koa'
import helmet from 'koa-helmet'
import serve from 'koa-static'
import { isBehindProxy } from './config'
import { allowedGroups, isBehindProxy } from './config'
import {
checkVerificationLink,
finishIntegration,
initializeIntegration,
} from './controllers/integration'
import { getStatus } from './controllers/status'
import { webhookReceiver } from './controllers/webhookReceiver'
import { authorizeGroups } from './middlewares/authorizeGroup'
import { solidAuth } from './middlewares/solidAuth'
import { validateBody } from './middlewares/validate'

Expand All @@ -20,42 +21,69 @@ app.proxy = isBehindProxy
const router = new Router()

router
// .post(
// '/inbox',
// solidAuth,
// /*
// #swagger.requestBody = {
// required: true,
// content: {
// 'application/json': {
// schema: {
// type: 'object',
// properties: {
// '@context': { const: 'https://www.w3.org/ns/activitystreams' },
// '@id': { type: 'string' },
// '@type': { const: 'Add' },
// actor: { type: 'string', format: 'uri' },
// object: { type: 'string', format: 'uri' },
// target: { type: 'string', format: 'email' },
// },
// required: ['@context', '@type', 'actor', 'object', 'target'],
// additionalProperties: false,
// },
// },
// },
// }
// */
// validateBody({
// type: 'object',
// properties: {
// '@context': { const: 'https://www.w3.org/ns/activitystreams' },
// '@id': { type: 'string' },
// '@type': { const: 'Add' },
// actor: { type: 'string', format: 'uri' },
// object: { type: 'string', format: 'uri' },
// target: { type: 'string', format: 'email' },
// },
// required: ['@context', '@type', 'actor', 'object', 'target'],
// additionalProperties: false,
// }),
// initializeIntegration,
// )
.post(
'/inbox',
'/init',
solidAuth,
/*
#swagger.requestBody = {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
properties: {
'@context': { const: 'https://www.w3.org/ns/activitystreams' },
'@id': { type: 'string' },
'@type': { const: 'Add' },
actor: { type: 'string', format: 'uri' },
object: { type: 'string', format: 'uri' },
target: { type: 'string', format: 'email' },
},
required: ['@context', '@type', 'actor', 'object', 'target'],
additionalProperties: false,
},
},
},
}
*/
authorizeGroups(allowedGroups),
// #swagger.requestBody = {
// required: true,
// content: {
// 'application/json': {
// schema: {
// type: 'object',
// properties: {
// email: { type: 'string', format: 'email' },
// },
// required: ['email'],
// additionalProperties: false,
// },
// },
// },
// }
validateBody({
type: 'object',
properties: {
'@context': { const: 'https://www.w3.org/ns/activitystreams' },
'@id': { type: 'string' },
'@type': { const: 'Add' },
actor: { type: 'string', format: 'uri' },
object: { type: 'string', format: 'uri' },
target: { type: 'string', format: 'email' },
},
required: ['@context', '@type', 'actor', 'object', 'target'],
properties: { email: { type: 'string', format: 'email' } },
required: ['email'],
additionalProperties: false,
}),
initializeIntegration,
Expand All @@ -71,7 +99,7 @@ app
bodyParser({
enableTypes: ['text', 'json'],
extendTypes: {
json: ['application/ld+json'],
json: ['application/ld+json', 'application/json'],
text: ['text/turtle'],
},
}),
Expand Down
21 changes: 19 additions & 2 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export const mailerCredentials = {
email: process.env.MAILER_IDENTITY_EMAIL ?? 'bot@example',
password: process.env.MAILER_IDENTITY_PASSWORD ?? 'password',
provider: process.env.MAILER_IDENTITY_PROVIDER ?? 'http://localhost:3456',
webId:
process.env.MAILER_IDENTITY_WEBID ??
'http://localhost:3456/bot/profile/card#me',
}

const stringToBoolean = (value: string | undefined): boolean => {
Expand All @@ -39,8 +42,8 @@ export const emailSender = process.env.EMAIL_SENDER

export const port: number = +(process.env.PORT ?? 3005)

// email verification expiration in milliseconds (1 hour)
export const emailVerificationExpiration = 3600 * 1000
// email verification expiration in seconds (1 hour)
export const emailVerificationExpiration = 3600

// configuration of database in form of sequelize options
export const database: Options = {
Expand All @@ -54,3 +57,17 @@ export const database: Options = {
}

export const isBehindProxy = stringToBoolean(process.env.BEHIND_PROXY)

const stringToArray = (value: string | undefined) => {
if (!value) return []
return value.split(/\s*,\s*/)
}

export const allowedGroups = stringToArray(
process.env.ALLOWED_GROUPS ?? 'https://example.com#us',
)

export const jwt = {
key: process.env.JWT_KEY ?? './ecdsa-p256-private.pem',
alg: process.env.JWT_ALG ?? 'ES256',
}
Loading

0 comments on commit 89753f7

Please sign in to comment.