diff --git a/package.json b/package.json index fef2f0a5..f618f933 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "license": "MIT", "scripts": { "build": "npx tsc", - "start": "node dist/server.js", + "start": "node dist/src/server.js", + "start:micro-rp": "node dist/tests/acceptance/micro-rp/app.js", "check:lint": "eslint . --ext .ts,.js", "check:pretty": "prettier --check \"*/**/*.{ts,json}\"", "check": "npm run check:lint && npm run check:pretty", diff --git a/tests/acceptance/micro-rp/app.ts b/tests/acceptance/micro-rp/app.ts new file mode 100644 index 00000000..cdb6b33c --- /dev/null +++ b/tests/acceptance/micro-rp/app.ts @@ -0,0 +1,91 @@ +import express, { Request, Response } from "express"; +import { logger } from "../../../src/logger"; +import { Config } from "../../../src/config"; +import { randomUUID } from "crypto"; +import { importPKCS8, SignJWT } from "jose"; + +const app = express(); +const port = 3001 +const config = Config.getInstance(); + +app.get("/callback", async (req: Request, res: Response) => { + const tokenResponse = await makeTokenRequest(req.params["code"]); + const userInfoResponse = await makeUserInfoRequest(tokenResponse["access_token"]); + + res.send(` + + Example - GOV.UK - User Info + + ${JSON.stringify(userInfoResponse)} + + + `); +}) + +app.listen(port, () => { + logger.info(`Micro RP listening on port ${port}`); +}); + +const makeTokenRequest = async (code: string): Promise => { + const privateKey = `-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCZddHcSxG9QxWE +Qky1DXB7EmN9DTRDQxDsBF9KE3ncGc5AQ8WdL8jye0F12Qp0Ter68xMjvbDoW/dK +wwz5yJHYsgd0RB8qCwu3o8Y1xXWQboYb/edJbemxbzDrlDd+bLzU/Xvjyp7MOtV9 +oasXg8NcAvNfWmH6fPyakqZyTppjosRQngH5Mh9jOUqabAV7euLSylJb4nnAT2aZ +PdWRHbJK4dFgdCXGUX119fuW05OxuAkXM3pM7TVlAOFjSIvDN0bCFLE7pHo39kg1 +gcIoU9pAP610qyvDtAMhifHQiJcWeQaeLOXdS2pKZcBpx5O88zY/PzjHYmLAeunZ +KaXpFgsJAgMBAAECggEAGtEkgb8ak/zPm0OsvOzizZb6jMVMbz6ei+f9sOezYVuf +F8rgEyZhEsKoP0xUz9s352+n1hSVgB1mGwn30ASVPA1sUQyAd6vjec1kW0wszbcK +t4SIsOPEtU2NenV1tyGQZBYB2t4zHtRfL2ubhunvLzqSxgR437mMuQRMkugagbOQ +CRPhwslZECcZvmOh5HURkbE0L5F1uXckc+tf0hktgiI4LB+Eej9e4TkhHnv6B9pe +yejfk/O+48O3sZ2emYgY6TSqcgwutj6UipROknyAorWUQ8vTaSewm6HO++cRH5a/ +D0JPoLX7uM8JcosIIiLE1p6qihjhPRe65Rvb7tSMOwKBgQDQBMHkETsQlb26vGhm +9Fc29GQJFe0yTZVD/94U98hpfbOu22E3TslXzpsNoCR61zgZdM7dWQINi3AvonFS +QJlDEYGNX0zYOqT1goI+3tBMpptnNzfgRN72bp748JiUyWLnjcWUNc2gwIEc2yET +wR4Zxz6A7h1iA9+fM/rEE1ULHwKBgQC823VoUO7p13WvdrYrmM93Xc6Cv5nZFLZn +bFjt4xwi096yJ2BTxARFhCBYaDD9vi4yzKjHih/1G53T6aaRbuLaNOSO58jHY1eh +par1Xw+JjKwK7bnFGOY+mGAT9kz/agDQv+ELu6PpgiRW/Awiz9UW5OV0cquQIhRj +60yn25PM1wKBgQCI2YhhLUDJUWnHbunUSY0S90bUf1tTy5yWZr9I1hY/6FWMhID5 +bNii7qYtGZzGP86FWMY68rKaDJDalaitrxfk+qBbTEX2vuYFKj3bdKReuQDlr3sQ +DN8OCoqFRWtr/u0VXryMG7VSuzJ1tGeXYmYWGXEySvSDpf648u5XjkxViwKBgQCO ++9COJAhePuQ47jXKGC2q//ikARAnzIi1ENDbeoEI1UPbufgyM0vQndInXOsKkXxE +tbJrMGY1mq0JjfKwVTWnYzhQAah/XPUxy0396/TFfR2cQJPPZ6Saa58CPg3ZqpXn +df6adXwKBKAiwz0k9hks9ivK2C6QN10csT8eLx5djQKBgQCiVnIJ3JcjNXHlygCW +eZG4zLcylZXusOv3VYBJKypBLVI74buoFfrvMcV/lQrI3Yo+6V95rNYGm+2MVxIc +iZSejbyqjUjJBAH9GHkPsiA+w1vutdd2PuPKOV05TLmV5ZM06bmLHQjMCGMiWK0G +8qVxFvr2NWRDB3otAjxVHR/ZQA== +-----END PRIVATE KEY-----` + const tokenUri = `${config.getSimulatorUrl()}/token`; + const claims = { + aud: tokenUri, + iss: config.getClientId(), + sub: config.getSub(), + exp: (Date.now() / 1000) + 5 * 60, + iat: Date.now() / 1000, + jti: randomUUID() + } + const tokenSigningKey = await importPKCS8(privateKey, "RSA"); + const jwt = await new SignJWT(claims).sign(tokenSigningKey); + const body = { + code: code, + redirect_uri: "http://localhost:3001/callback", + client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + client_assertion: jwt, + grant_type: "authorization_code" + }; + const response = await fetch(tokenUri, { "method": "POST", "body": new URLSearchParams(body).toString() } ); + return await response.json(); +} + +const makeUserInfoRequest = async (accessToken: string) => { + const userInfoResponse = await fetch(`${config.getSimulatorUrl()}/userinfo`, { headers: { "Authorization": `Bearer ${accessToken}` } }) + + return await userInfoResponse.json(); +} + +interface TokenResponse { + access_token: string, + token_type: string, + expires_in: number, + id_token: string +} diff --git a/tsconfig.json b/tsconfig.json index 8868e279..3e4b30a2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,8 +8,10 @@ "sourceMap": true, "outDir": "dist", "baseUrl": ".", - "typeRoots": ["@types", "node_modules/@types"] + "typeRoots": ["@types", "node_modules/@types"], }, - "include": ["src/**/*.ts"], - "exclude": ["tests", "coverage", "src/**/**/tests"] + "include": ["src/**/*.ts", + "tests/acceptance/micro-rp/*.ts" + ], + "exclude": ["coverage", "src/**/**/tests"] }