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

feat(auth): use split auth specs in app #692

Merged
merged 14 commits into from
Nov 8, 2022
Merged
1 change: 1 addition & 0 deletions .github/workflows/lint_test_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ jobs:
- uses: actions/setup-node@v2
- uses: ./.github/workflows/rafiki/env-setup
- run: pnpm --filter openapi build
- run: pnpm --filter auth fetch-schemas
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I wonder if fetch-schemas also needs to be run in the Dockerfile
https://github.com/interledger/rafiki/blob/main/packages/auth/Dockerfile#L18

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, local env fails as is with:

local-peer-auth-1     | {"level":50,"time":1667568836819,"pid":1,"hostname":"f5cbb380f8b7","msg":"ResolverError: Error opening file \"/workspace/openapi/auth-server.yaml\" \nENOENT: no such file or directory, open '/workspace/openapi/auth-server.yaml'\n    at ReadFileContext.callback (/workspace/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/lib/resolvers/file.js:52:20)\n    at FSReqCallback.readFileAfterOpen [as oncomplete] (node:fs:323:13)"}

I'm thinking instead of adding fetch-schemas to the Dockerfile, we just add it to https://github.com/interledger/rafiki#environment-setup
That way localenv can run with modified specs.

- run: pnpm --filter auth test
- run: pnpm --filter auth run fetch-schemas
- name: AsyncAPI extension
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ pnpm --filter open-api test
# run all tests
pnpm -r --workspace-concurrency=1 test

# pull in latest openapi specs for auth server:
pnpm --filter auth fetch-schemas

# format and lint code:
pnpm format

Expand Down
3 changes: 2 additions & 1 deletion packages/auth/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/openapi/schemas.yaml
/src/openapi/schemas.yaml
/src/openapi/auth-server.yaml
5 changes: 3 additions & 2 deletions packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
"scripts": {
"knex": "knex",
"build:deps": "pnpm --filter openapi build",
"build": "pnpm build:deps && tsc --build tsconfig.json",
"build": "pnpm build:deps && tsc --build tsconfig.json && pnpm copy-files",
"clean": "rm -fr dist/",
"fetch-schemas": "./scripts/get-op-schema.sh",
"test": "jest --passWithNoTests --maxWorkers=50%",
"prepack": "pnpm build"
"prepack": "pnpm build",
"copy-files": "cp -r ./src/openapi ./dist"
},
"dependencies": {
"@adonisjs/fold": "^8.1.0",
Expand Down
7 changes: 4 additions & 3 deletions packages/auth/scripts/get-op-schema.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symli
SOURCE=$(readlink "$SOURCE")
[[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
OUTDIR=$( cd -P "$( dirname "$SOURCE" )/../openapi" >/dev/null 2>&1 && pwd )

curl -o "$OUTDIR/schemas.yaml" https://raw.githubusercontent.com/interledger/open-payments/main/openapi/schemas.yaml
OUTDIR=$( cd -P "$( dirname "$SOURCE" )/../src/openapi" >/dev/null 2>&1 && pwd )

# TODO: revert to using main once https://github.com/interledger/rafiki/issues/630 is resolved
curl -o "$OUTDIR/schemas.yaml" https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/schemas.yaml
curl -o "$OUTDIR/auth-server.yaml" https://raw.githubusercontent.com/interledger/open-payments/62c4b4a9875e3adaa21f89f597e88db43016fe0b/openapi/auth-server.yaml
14 changes: 11 additions & 3 deletions packages/auth/src/accessToken/routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ describe('Access Token Routes', (): void => {
appContainer = await createTestApp(deps)
knex = await deps.use('knex')
accessTokenRoutes = await deps.use('accessTokenRoutes')
jestOpenAPI(await deps.use('openApi'))
const openApi = await deps.use('openApi')
jestOpenAPI(openApi.authServerSpec)

const keys = await generateTestKeys()
testJwk = keys.publicKey
Expand Down Expand Up @@ -67,10 +68,11 @@ describe('Access Token Routes', (): void => {

const BASE_ACCESS = {
type: AccessType.OutgoingPayment,
actions: [Action.Read, Action.Create],
actions: [Action.Read, Action.Create, Action.List],
identifier: `https://example.com/${v4()}`,
limits: {
receiver: 'https://wallet.com/alice',
receiver:
'https://wallet.com/alice/incoming-payments/12341234-1234-1234-1234-123412341234',
sendAmount: {
value: '400',
assetCode: 'USD',
Expand Down Expand Up @@ -106,6 +108,9 @@ describe('Access Token Routes', (): void => {
grantId: grant.id,
...BASE_TOKEN
})

const openApi = await deps.use('openApi')
jestOpenAPI(openApi.resourceServerSpec)
})
test('Cannot introspect fake token', async (): Promise<void> => {
const ctx = createContext(
Expand Down Expand Up @@ -354,6 +359,9 @@ describe('Access Token Routes', (): void => {
...BASE_TOKEN
})
managementId = BASE_TOKEN.managementId

const openApi = await deps.use('openApi')
jestOpenAPI(openApi.authServerSpec)
})

test('Cannot rotate nonexistent token', async (): Promise<void> => {
Expand Down
18 changes: 9 additions & 9 deletions packages/auth/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export class App {
// Grant Initiation
this.publicRouter.post(
'/',
createValidatorMiddleware(openApi, {
createValidatorMiddleware(openApi.authServerSpec, {
path: '/',
method: HttpMethod.POST
}),
Expand All @@ -202,7 +202,7 @@ export class App {
// Grant Continue
this.publicRouter.post(
'/continue/:id',
createValidatorMiddleware(openApi, {
createValidatorMiddleware(openApi.authServerSpec, {
path: '/continue/{id}',
method: HttpMethod.POST
}),
Expand All @@ -213,7 +213,7 @@ export class App {
// Token Rotation
this.publicRouter.post(
'/token/:id',
createValidatorMiddleware(openApi, {
createValidatorMiddleware(openApi.authServerSpec, {
path: '/token/{id}',
method: HttpMethod.POST
}),
Expand All @@ -224,7 +224,7 @@ export class App {
// Token Revocation
this.publicRouter.delete(
'/token/:id',
createValidatorMiddleware(openApi, {
createValidatorMiddleware(openApi.authServerSpec, {
path: '/token/{id}',
method: HttpMethod.DELETE
}),
Expand All @@ -236,7 +236,7 @@ export class App {
// Token Introspection
this.publicRouter.post(
'/introspect',
createValidatorMiddleware(openApi, {
createValidatorMiddleware(openApi.resourceServerSpec, {
path: '/introspect',
method: HttpMethod.POST
}),
Expand All @@ -249,7 +249,7 @@ export class App {
// Interaction start
this.publicRouter.get(
'/interact/:id/:nonce',
createValidatorMiddleware(openApi, {
createValidatorMiddleware(openApi.idpSpec, {
path: '/interact/{id}/{nonce}',
method: HttpMethod.GET
}),
Expand All @@ -259,7 +259,7 @@ export class App {
// Interaction finish
this.publicRouter.get(
'/interact/:id/:nonce/finish',
createValidatorMiddleware(openApi, {
createValidatorMiddleware(openApi.idpSpec, {
path: '/interact/{id}/{nonce}/finish',
method: HttpMethod.GET
}),
Expand All @@ -269,7 +269,7 @@ export class App {
// Grant lookup
this.publicRouter.get(
'/grant/:id/:nonce',
createValidatorMiddleware(openApi, {
createValidatorMiddleware(openApi.idpSpec, {
path: '/grant/{id}/{nonce}',
method: HttpMethod.GET
}),
Expand All @@ -279,7 +279,7 @@ export class App {
// Grant accept/reject
this.publicRouter.post(
'/grant/:id/:nonce/:choice',
createValidatorMiddleware(openApi, {
createValidatorMiddleware(openApi.idpSpec, {
path: '/grant/{id}/{nonce}/{choice}',
method: HttpMethod.POST
}),
Expand Down
6 changes: 1 addition & 5 deletions packages/auth/src/config/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,9 @@ export const Config = {
'AUTH_DATABASE_URL',
'postgresql://postgres:password@localhost:5432/auth_development'
),
authServerSpec: envString(
'AUTH_SERVER_SPEC',
'https://raw.githubusercontent.com/interledger/open-payments/16cff7a2605cb90d0e5b5d34ba32c8263d694f2b/auth-server-open-api-spec.yaml'
),
identityServerDomain: envString(
'IDENTITY_SERVER_DOMAIN',
'http://localhost:3004'
'http://localhost:3300'
),
identityServerSecret: envString('IDENTITY_SERVER_SECRET', 'replace-me'),
authServerDomain: envString('AUTH_SERVER_DOMAIN', 'http://localhost:3006'), // TODO: replace this with whatever frontend port ends up being
Expand Down
11 changes: 10 additions & 1 deletion packages/auth/src/grant/routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ describe('Grant Routes', (): void => {
config = await deps.use('config')
knex = await deps.use('knex')
appContainer = await createTestApp(deps)
jestOpenAPI(await deps.use('openApi'))
const openApi = await deps.use('openApi')
jestOpenAPI(openApi.authServerSpec)
accessTokenService = await deps.use('accessTokenService')
})

Expand Down Expand Up @@ -360,6 +361,10 @@ describe('Grant Routes', (): void => {

// TODO: validate that routes satisfy API spec
describe('interaction', (): void => {
beforeEach(async (): Promise<void> => {
const openApi = await deps.use('openApi')
jestOpenAPI(openApi.idpSpec)
})
describe('interaction start', (): void => {
test('Interaction start fails if grant is invalid', async (): Promise<void> => {
const scope = nock(KEY_REGISTRY_ORIGIN)
Expand Down Expand Up @@ -842,6 +847,10 @@ describe('Grant Routes', (): void => {
})

describe('/continue', (): void => {
beforeEach(async (): Promise<void> => {
const openApi = await deps.use('openApi')
jestOpenAPI(openApi.authServerSpec)
})
test('Can issue access token', async (): Promise<void> => {
const grant = await Grant.query().insert({
...generateBaseGrant(),
Expand Down
19 changes: 16 additions & 3 deletions packages/auth/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from 'path'
import { EventEmitter } from 'events'
import createLogger from 'pino'
import { knex } from 'knex'
Expand Down Expand Up @@ -102,9 +103,21 @@ export function initIocContainer(
})
})

container.singleton('openApi', async (deps) => {
const config = await deps.use('config')
return await createOpenAPI(config.authServerSpec)
container.singleton('openApi', async () => {
const authServerSpec = await createOpenAPI(
path.resolve(__dirname, './openapi/auth-server.yaml')
)
const resourceServerSpec = await createOpenAPI(
path.resolve(__dirname, './openapi/resource-server.yaml')
)
const idpSpec = await createOpenAPI(
path.resolve(__dirname, './openapi/id-provider.yaml')
)
return {
authServerSpec,
resourceServerSpec,
idpSpec
}
})

container.singleton(
Expand Down