Skip to content

Commit

Permalink
Include additional data in token payloads of verification and proof JWTs
Browse files Browse the repository at this point in the history
  • Loading branch information
mrkvon committed Jan 15, 2024
1 parent 6501e88 commit d4787bb
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 13 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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=
Expand Down
3 changes: 3 additions & 0 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 Down
22 changes: 17 additions & 5 deletions src/controllers/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ export const initializeIntegration: Middleware = async ctx => {
const tokenExpiration = config.emailVerificationExpiration

const pem = await readFile(config.jwt.key, { encoding: 'utf-8' })
const jwt = jsonwebtoken.sign({ webId: user, email }, pem, {
algorithm: 'ES256',
expiresIn: tokenExpiration,
})
const jwt = jsonwebtoken.sign(
{
webId: user,
email,
emailVerified: false,
iss: config.mailerCredentials.webId,
},
pem,
{ algorithm: 'ES256', expiresIn: tokenExpiration },
)

const emailVerificationLink = `${config.baseUrl}/verify-email?token=${jwt}`

Expand Down Expand Up @@ -49,6 +55,8 @@ export const checkVerificationLink: Middleware = async (ctx, next) => {
const payload = jsonwebtoken.verify(jwt, pem) as {
webId: string
email: string
emailVerified: boolean
iss: string
iat: number
exp: number
}
Expand All @@ -73,7 +81,11 @@ export const finishIntegration: Middleware = async ctx => {
}

const pem = await readFile(config.jwt.key, { encoding: 'utf-8' })
const jwt = jsonwebtoken.sign({ webId, email }, pem, { algorithm: 'ES256' })
const jwt = jsonwebtoken.sign(
{ webId, email, emailVerified: true, iss: config.mailerCredentials.webId },
pem,
{ algorithm: 'ES256' },
)
ctx.response.body = jwt
ctx.set('content-type', 'text/plain')
ctx.response.status = 200
Expand Down
29 changes: 21 additions & 8 deletions src/test/integration-finish.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { expect } from 'chai'
import * as cheerio from 'cheerio'
import fetch from 'cross-fetch'
import * as jsonwebtoken from 'jsonwebtoken'
import { describe } from 'mocha'
import Mail from 'nodemailer/lib/mailer'
import { SinonSandbox, SinonSpy, createSandbox } from 'sinon'
import { baseUrl } from '../config'
import * as config from '../config'
import * as mailerService from '../services/mailerService'
import { authenticatedFetch } from './testSetup.spec'
import { authenticatedFetch, person } from './testSetup.spec'

describe('email verification via /verify-email?token=jwt', () => {
let sendMailSpy: SinonSpy<[options: Mail.Options], Promise<void>>
Expand All @@ -25,7 +26,7 @@ describe('email verification via /verify-email?token=jwt', () => {

beforeEach(async () => {
// initialize the integration
const initResponse = await authenticatedFetch(`${baseUrl}/init`, {
const initResponse = await authenticatedFetch(`${config.baseUrl}/init`, {
method: 'post',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email: '[email protected]' }),
Expand All @@ -44,10 +45,22 @@ describe('email verification via /verify-email?token=jwt', () => {
expect(response.status).to.equal(200)
})

it(
'[correct token] should return proof of verification for the user to save on their pod, or edit the pod directly',
)
// this is a JWT token, it keeps email and webId of the person as payload
it('[correct token] should return proof of verification for the user to save on their pod', async () => {
// the proof is a JWT token, it keeps email and webId of the person as payload
const response = await fetch(verificationLink)
expect(response.ok).to.be.true
const jwt = await response.text()
const payload = jsonwebtoken.decode(jwt) as jsonwebtoken.JwtPayload

expect(payload).to.include({
iss: config.mailerCredentials.webId,
webId: person.webId,
email: '[email protected]',
emailVerified: true,
})
})

it("[correct token] (maybe) should save the verification proof to user's pod")

it('[incorrect token] should respond with 400', async () => {
const response = await fetch(
Expand All @@ -58,7 +71,7 @@ describe('email verification via /verify-email?token=jwt', () => {
})

it('[missing token] should respond with 400', async () => {
const response = await fetch(`${baseUrl}/verify-email`)
const response = await fetch(`${config.baseUrl}/verify-email`)
expect(response.status).to.equal(400)
expect(await response.text()).to.equal(
'This is not a valid verification link. Have you received the link in your email?',
Expand Down

0 comments on commit d4787bb

Please sign in to comment.