diff --git a/packages/core/package.json b/packages/core/package.json index a647de9f0..46acf5be2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -20,7 +20,7 @@ "koa-mount": "^4.0.0", "koa-router": "^10.0.0", "oidc-provider": "^7.4.1", - "slonik": "23.6.3", + "slonik": "^23.8.1", "slonik-interceptor-preset": "^1.2.10" }, "devDependencies": { diff --git a/packages/core/src/database/pool.ts b/packages/core/src/database/pool.ts index c28278d14..c89bb3b76 100644 --- a/packages/core/src/database/pool.ts +++ b/packages/core/src/database/pool.ts @@ -1,8 +1,9 @@ import { createPool } from 'slonik'; import { createInterceptors } from 'slonik-interceptor-preset'; +import { getEnv } from '../utils'; const interceptors = [...createInterceptors()]; -const pool = createPool('postgres://localhost/logto', { interceptors }); +const pool = createPool(getEnv('DB_URL'), { interceptors }); export default pool; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 8589e8bd1..4866f386b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -2,54 +2,16 @@ import dotenv from 'dotenv'; dotenv.config(); import Koa from 'koa'; -import logger from 'koa-logger'; -import mount from 'koa-mount'; -import Router from 'koa-router'; -import { Provider } from 'oidc-provider'; -import postgresAdapter from './oidc/adapter'; - -const router = new Router(); +import initApp from './init'; +import { getEnv } from './utils'; const app = new Koa(); -const PORT = 3000; +const port = Number(getEnv('PORT', '3000')); -const oidc = new Provider(`http://localhost:${PORT}/oidc`, { - adapter: postgresAdapter, - renderError: (ctx, out, error) => { - console.log(error); - }, - cookies: { - // V2: Rotate this when necessary - // https://github.com/panva/node-oidc-provider/blob/main/docs/README.md#cookieskeys - keys: ['LOGTOSEKRIT1'], - }, - clients: [ - { - client_id: 'foo', - redirect_uris: ['http://localhost:3000/callback'], - grant_types: ['authorization_code', 'refresh_token'], - token_endpoint_auth_method: 'none', - }, - ], - findAccount: (ctx, sub) => { - console.log('finding account'); - return { - accountId: sub, - claims: async (use, scope, claims) => { - console.log('claims', use, scope, claims); - return { sub }; - }, - }; - }, -}); - -router.get('/callback', (ctx) => { - ctx.body = 'A callback'; -}); - -// eslint-disable-next-line @typescript-eslint/no-unsafe-argument -app.use(logger()).use(mount('/oidc', oidc.app)).use(router.routes()).use(router.allowedMethods()); - -app.listen(PORT, () => { - console.log(`App is listening on port ${PORT}`); -}); +(async () => { + try { + await initApp(app, port); + } catch (error: unknown) { + console.log('Error while initializing app', error); + } +})(); diff --git a/packages/core/src/init/index.ts b/packages/core/src/init/index.ts new file mode 100644 index 000000000..0deb69a93 --- /dev/null +++ b/packages/core/src/init/index.ts @@ -0,0 +1,16 @@ +import Koa from 'koa'; +import logger from 'koa-logger'; + +import initOidc from './oidc'; +import initRouter from './router'; + +export default async function initApp(app: Koa, port: number): Promise { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + app.use(logger()); + await initOidc(app, port); + initRouter(app); + + app.listen(port, () => { + console.log(`App is listening on port ${port}`); + }); +} diff --git a/packages/core/src/init/oidc.ts b/packages/core/src/init/oidc.ts new file mode 100644 index 000000000..0fce1a99e --- /dev/null +++ b/packages/core/src/init/oidc.ts @@ -0,0 +1,48 @@ +import crypto from 'crypto'; +import Koa from 'koa'; +import mount from 'koa-mount'; +import { Provider } from 'oidc-provider'; +import postgresAdapter from '../oidc/adapter'; + +import { fromKeyLike } from 'jose/jwk/from_key_like'; +import { getEnv } from '../utils'; + +export default async function initOidc(app: Koa, port: number): Promise { + const privateKey = crypto.createPrivateKey( + Buffer.from(getEnv('OIDC_PROVIDER_PRIVATE_KEY_BASE64'), 'base64') + ); + const keys = [await fromKeyLike(privateKey)]; + const oidc = new Provider(`http://localhost:${port}/oidc`, { + adapter: postgresAdapter, + renderError: (ctx, out, error) => { + console.log(error); + }, + cookies: { + // V2: Rotate this when necessary + // https://github.com/panva/node-oidc-provider/blob/main/docs/README.md#cookieskeys + keys: ['LOGTOSEKRIT1'], + }, + jwks: { + keys, + }, + clients: [ + { + client_id: 'foo', + redirect_uris: ['http://localhost:3000/callback'], + grant_types: ['authorization_code', 'refresh_token'], + token_endpoint_auth_method: 'none', + }, + ], + findAccount: (ctx, sub) => { + console.log('finding account'); + return { + accountId: sub, + claims: async (use, scope, claims) => { + console.log('claims', use, scope, claims); + return { sub }; + }, + }; + }, + }); + app.use(mount('/oidc', oidc.app)); +} diff --git a/packages/core/src/init/router.ts b/packages/core/src/init/router.ts new file mode 100644 index 000000000..b7c35d86e --- /dev/null +++ b/packages/core/src/init/router.ts @@ -0,0 +1,12 @@ +import Koa from 'koa'; +import Router from 'koa-router'; + +const router = new Router(); + +router.get('/callback', (ctx) => { + ctx.body = 'A callback'; +}); + +export default function initRouter(app: Koa): void { + app.use(router.routes()).use(router.allowedMethods()); +} diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 256d2b5ef..9fcaeeece 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -3,3 +3,5 @@ export type Falsy = 0 | undefined | null | false | ''; export const conditional = (value: T | Falsy): Optional => (value ? value : undefined); export const conditionalString = (value: string | Falsy): string => (value ? value : ''); + +export const getEnv = (key: string, fallback = ''): string => process.env[key] ?? fallback; diff --git a/packages/core/yarn.lock b/packages/core/yarn.lock index 6325e1a40..b997ec63f 100644 --- a/packages/core/yarn.lock +++ b/packages/core/yarn.lock @@ -3940,7 +3940,7 @@ pg-copy-streams@^5.1.1: dependencies: obuf "^1.1.2" -pg-cursor@^2.4.1, pg-cursor@^2.5.2: +pg-cursor@^2.4.1, pg-cursor@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/pg-cursor/-/pg-cursor-2.6.0.tgz#a85df1bd1389c75ffa443ee94073da0a1be360ba" integrity sha512-BFLg40CTgBJ+LX9EwqjztUYaKxpxLffMmDTmlQNMCustX/JxMTYimxRkdhZvPYZGp++/2LjuqkKtO5DVVq0FNg== @@ -3995,7 +3995,7 @@ pg-types@^3.0.1: postgres-date "~1.0.4" postgres-interval "^1.1.0" -pg@^8.4.1, pg@^8.5.1: +pg@^8.4.1, pg@^8.6.0: version "8.6.0" resolved "https://registry.yarnpkg.com/pg/-/pg-8.6.0.tgz#e222296b0b079b280cce106ea991703335487db2" integrity sha512-qNS9u61lqljTDFvmk/N66EeGq3n6Ujzj0FFyNMGQr6XuEv4tgNTXvJQTfJdcvGit5p5/DWPu+wj920hAJFI+QQ== @@ -4427,7 +4427,7 @@ roarr@^2.15.4: semver-compare "^1.0.0" sprintf-js "^1.1.2" -roarr@^4.2.0: +roarr@^4.2.5: version "4.2.5" resolved "https://registry.yarnpkg.com/roarr/-/roarr-4.2.5.tgz#b4e5ddba4ef40ce66fbef0dc57cf9c584687a7cb" integrity sha512-ZSs1hr2gyWickWDr2Yw0qcuef+EJKwZtNxUj7poxvIDxVq+ZvQreVNdPVLHonWpavBeZaOcAGVFV5xM/HqRR8g== @@ -4520,7 +4520,7 @@ serialize-error@^7.0.1: dependencies: type-fest "^0.13.1" -serialize-error@^8.0.1: +serialize-error@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== @@ -4646,30 +4646,6 @@ slonik-interceptor-query-normalisation@^1.1.10: core-js "^3.6.4" slonik "^22.4.0" -slonik@23.6.3: - version "23.6.3" - resolved "https://registry.yarnpkg.com/slonik/-/slonik-23.6.3.tgz#84c7853463d9980f8efc92ca90c3389d838e639a" - integrity sha512-o+GtTFfXDYf5m1Hx2Bz1mGHxk6VRGOxALg6Arr6vVfQu//ua6j9YI/0/O21ul1LP0wIQTrdmpzoma3qAfhXTOQ== - dependencies: - concat-stream "^2.0.0" - delay "^5.0.0" - es6-error "^4.1.1" - get-stack-trace "^2.0.3" - hyperid "^2.1.0" - is-plain-object "^5.0.0" - iso8601-duration "^1.3.0" - pg "^8.5.1" - pg-connection-string "^2.4.0" - pg-copy-streams "^5.1.1" - pg-copy-streams-binary "^2.0.1" - pg-cursor "^2.5.2" - postgres-array "^3.0.1" - postgres-interval "^3.0.0" - promise-deferred "^2.0.3" - roarr "^4.2.0" - serialize-error "^8.0.1" - through2 "^4.0.2" - slonik@^22.4.0: version "22.7.1" resolved "https://registry.yarnpkg.com/slonik/-/slonik-22.7.1.tgz#46326b3050f3e466918a7db3eacc62dc40834ea5" @@ -4695,6 +4671,30 @@ slonik@^22.4.0: through2 "^4.0.2" ulid "^2.3.0" +slonik@^23.8.1: + version "23.8.1" + resolved "https://registry.yarnpkg.com/slonik/-/slonik-23.8.1.tgz#594016f5fa8755fa95b1e295dec2eefe94c8e05d" + integrity sha512-TUWOddia5kNfI641EPeo4AMBwBXb+IfW2zgHKULcc2+rOtL6toM6EBGoxAbMrfgI0Djku/+Fkui+Uo1mi8KGoA== + dependencies: + concat-stream "^2.0.0" + delay "^5.0.0" + es6-error "^4.1.1" + get-stack-trace "^2.0.3" + hyperid "^2.1.0" + is-plain-object "^5.0.0" + iso8601-duration "^1.3.0" + pg "^8.6.0" + pg-connection-string "^2.5.0" + pg-copy-streams "^5.1.1" + pg-copy-streams-binary "^2.0.1" + pg-cursor "^2.6.0" + postgres-array "^3.0.1" + postgres-interval "^3.0.0" + promise-deferred "^2.0.3" + roarr "^4.2.5" + serialize-error "^8.1.0" + through2 "^4.0.2" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"