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 service-to-service api #3148

Merged
merged 23 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f48bc8f
feat(auth): add service api with /healtz endpoint
BlairCurrey Dec 3, 2024
e593b2a
feat(auth): tenant routes
BlairCurrey Dec 3, 2024
cab3e2c
feat(auth): service api error handling
BlairCurrey Dec 3, 2024
0ded2df
chore(auth): rm old todo
BlairCurrey Dec 3, 2024
7cc448d
fix(auth): how errors are set
BlairCurrey Dec 3, 2024
d2b6411
fix(auth): improve tenant tests, cleanup tenant get response,
BlairCurrey Dec 3, 2024
b3eda45
feat(backend): auth service api client
BlairCurrey Dec 5, 2024
ecae9f2
fix(auth): change status codes to 204 where no body
BlairCurrey Dec 5, 2024
8e51dff
fix(backend): format
BlairCurrey Dec 5, 2024
0bab0c4
feat(auth): add required deletedAt to DELETE /tenant body
BlairCurrey Dec 5, 2024
9ce0f4d
Merge branch '2893/multi-tenancy-v1' into bc/3125/auth-backend-servic…
BlairCurrey Dec 18, 2024
5fe1061
feat(backend): AUTH_SERVICE_API_URL env var
BlairCurrey Dec 18, 2024
dba2cd4
fix(backend): auth service client tests to mock codes correctly
BlairCurrey Dec 18, 2024
02cc1d5
feat(backend): add AuthServiceClient dep
BlairCurrey Dec 18, 2024
5635631
feat(backend): use auth service client in tenant service
BlairCurrey Dec 18, 2024
2742f1b
chore(auth): format
BlairCurrey Dec 20, 2024
a499775
chore(auth): format
BlairCurrey Dec 20, 2024
2f60a86
fix(integration,localenv): auth service api config
BlairCurrey Dec 20, 2024
0188f2f
fix(backend,auth): update tenant api to support deletedAt
BlairCurrey Dec 20, 2024
eebb862
docs: update with env vars
BlairCurrey Dec 20, 2024
2212f88
fix(backend): dep container type
BlairCurrey Dec 20, 2024
aa03813
fix(localenv): docker compose config
BlairCurrey Dec 20, 2024
8835a8c
fix(backend): add default header to api client
BlairCurrey Jan 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions localenv/cloud-nine-wallet/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ services:
AUTH_SERVER_INTROSPECTION_URL: http://cloud-nine-wallet-auth:3007
AUTH_ADMIN_API_URL: 'http://cloud-nine-wallet-auth:3003/graphql'
AUTH_ADMIN_API_SECRET: 'rPoZpe9tVyBNCigm05QDco7WLcYa0xMao7lO5KG1XG4='
AUTH_SERVICE_API_URL: 'http://cloud-nine-wallet-auth:3011'
ILP_ADDRESS: ${ILP_ADDRESS:-test.cloud-nine-wallet}
STREAM_SECRET: BjPXtnd00G2mRQwP/8ZpwyZASOch5sUXT5o0iR5b5wU=
API_SECRET: iyIgCprjb9uL8wFckR+pLEkJWMB7FJhgkvqhTQR/964=
Expand Down Expand Up @@ -108,6 +109,7 @@ services:
- '3006:3006'
- "9230:9229"
- '3009:3009'
- '3011:3011'
environment:
NODE_ENV: ${NODE_ENV:-development}
TRUST_PROXY: ${TRUST_PROXY}
Expand All @@ -119,6 +121,7 @@ services:
COOKIE_KEY: 42397d1f371dd4b8b7d0308a689a57c882effd4ea909d792302542af47e2cd37
ADMIN_API_SECRET: rPoZpe9tVyBNCigm05QDco7WLcYa0xMao7lO5KG1XG4=
OPERATOR_TENANT_ID: 438fa74a-fa7d-4317-9ced-dde32ece1787
SERVICE_API_PORT: 3011
depends_on:
- shared-database
- shared-redis
Expand Down
3 changes: 3 additions & 0 deletions localenv/happy-life-bank/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ services:
AUTH_SERVER_INTROSPECTION_URL: http://happy-life-bank-auth:3007
AUTH_ADMIN_API_URL: 'http://happy-life-bank-auth:4003/graphql'
AUTH_ADMIN_API_SECRET: 'rPoZpe9tVyBNCigm05QDco7WLcYa0xMao7lO5KG1XG4='
AUTH_SERVICE_API_URL: 'http://happy-life-bank-auth:4011'
ILP_ADDRESS: test.happy-life-bank
ILP_CONNECTOR_URL: http://happy-life-bank-backend:4002
STREAM_SECRET: BjPXtnd00G2mRQwP/8ZpwyZASOch5sUXT5o0iR5b5wU=
Expand Down Expand Up @@ -98,6 +99,7 @@ services:
- '4006:3006'
- '9232:9229'
- '4009:3009'
- '4011:4011'
environment:
NODE_ENV: development
AUTH_DATABASE_URL: postgresql://happy_life_bank_auth:happy_life_bank_auth@shared-database/happy_life_bank_auth
Expand All @@ -108,6 +110,7 @@ services:
COOKIE_KEY: 42397d1f371dd4b8b7d0308a689a57c882effd4ea909d792302542af47e2cd37
ADMIN_API_SECRET: rPoZpe9tVyBNCigm05QDco7WLcYa0xMao7lO5KG1XG4=
OPERATOR_TENANT_ID: cf5fd7d3-1eb1-4041-8e43-ba45747e9e5d
SERVICE_API_PORT: 4011
depends_on:
- cloud-nine-auth
happy-life-admin:
Expand Down
53 changes: 53 additions & 0 deletions packages/auth/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export class App {
private interactionServer!: Server
private introspectionServer!: Server
private adminServer!: Server
private serviceAPIServer!: Server
private logger!: Logger
private config!: IAppConfig
private databaseCleanupRules!: {
Expand Down Expand Up @@ -455,6 +456,51 @@ export class App {
this.interactionServer = koa.listen(port)
}

public async startServiceAPIServer(port: number | string): Promise<void> {
const koa = await this.createKoaServer()

const router = new Router<DefaultState, AppContext>()
router.use(bodyParser())

const errorHandler = async (ctx: Koa.Context, next: Koa.Next) => {
try {
await next()
} catch (err) {
const logger = await ctx.container.use('logger')
logger.info(
{
method: ctx.method,
route: ctx.path,
headers: ctx.headers,
params: ctx.params,
requestBody: ctx.request.body,
err
},
'Service API Error'
)
}
}

koa.use(errorHandler)

router.get('/healthz', (ctx: AppContext): void => {
ctx.status = 200
})

const tenantRoutes = await this.container.use('tenantRoutes')

router.get('/tenant/:id', tenantRoutes.get)
router.post('/tenant', tenantRoutes.create)
router.patch('/tenant/:id', tenantRoutes.update)
router.delete('/tenant/:id', tenantRoutes.delete)

koa.use(cors())
koa.use(router.middleware())
koa.use(router.routes())

this.serviceAPIServer = koa.listen(port)
}

private async createKoaServer(): Promise<Koa<Koa.DefaultState, AppContext>> {
const koa = new Koa<DefaultState, AppContext>({
proxy: this.config.trustProxy
Expand Down Expand Up @@ -500,6 +546,9 @@ export class App {
if (this.introspectionServer) {
await this.stopServer(this.introspectionServer)
}
if (this.serviceAPIServer) {
await this.stopServer(this.serviceAPIServer)
}
}

private async stopServer(server: Server): Promise<void> {
Expand Down Expand Up @@ -530,6 +579,10 @@ export class App {
return this.getPort(this.introspectionServer)
}

public getServiceAPIPort(): number {
return this.getPort(this.serviceAPIServer)
}

private getPort(server: Server): number {
const address = server?.address()
if (address && !(typeof address == 'string')) {
Expand Down
1 change: 1 addition & 0 deletions packages/auth/src/config/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const Config = {
authPort: envInt('AUTH_PORT', 3006),
interactionPort: envInt('INTERACTION_PORT', 3009),
introspectionPort: envInt('INTROSPECTION_PORT', 3007),
serviceAPIPort: envInt('SERVICE_API_PORT', 3011),
Copy link
Contributor

Choose a reason for hiding this comment

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

what do you think about calling this Tenant service instead?

Are you approaching this from this service being used for something else, for example?

Copy link
Contributor Author

@BlairCurrey BlairCurrey Jan 13, 2025

Choose a reason for hiding this comment

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

Are you approaching this from this service being used for something else, for example?

Yes, that was the idea. Figured it can serve the purpose of any service-to-service communication. No specific things in mind but if we did want to expose some other resource I dont imagine we'd want a new api just for that. And probably better to avoid renaming everything including this env var (since it would be breaking).

env: envString('NODE_ENV', 'development'),
trustProxy: envBool('TRUST_PROXY', false),
enableManualMigrations: envBool('ENABLE_MANUAL_MIGRATIONS', false),
Expand Down
14 changes: 14 additions & 0 deletions packages/auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { createInteractionService } from './interaction/service'
import { getTokenIntrospectionOpenAPI } from 'token-introspection'
import { Redis } from 'ioredis'
import { createTenantService } from './tenant/service'
import { createTenantRoutes } from './tenant/routes'

const container = initIocContainer(Config)
const app = new App(container)
Expand Down Expand Up @@ -163,6 +164,16 @@ export function initIocContainer(
}
)

container.singleton(
'tenantRoutes',
async (deps: IocContract<AppServices>) => {
return createTenantRoutes({
tenantService: await deps.use('tenantService'),
logger: await deps.use('logger')
})
}
)

container.singleton('openApi', async () => {
const authServerSpec = await getAuthServerOpenAPI()
const idpSpec = await createOpenAPI(
Expand Down Expand Up @@ -315,6 +326,9 @@ export const start = async (

await app.startIntrospectionServer(config.introspectionPort)
logger.info(`Introspection server listening on ${app.getIntrospectionPort()}`)

await app.startServiceAPIServer(config.serviceAPIPort)
logger.info(`Service API server listening on ${app.getServiceAPIPort()}`)
}

// If this script is run directly, start the server
Expand Down
17 changes: 16 additions & 1 deletion packages/auth/src/shared/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Config } from '../config/app'
import { createContext } from '../tests/context'
import { generateApiSignature } from '../tests/apiSignature'
import { initIocContainer } from '..'
import { verifyApiSignature } from './utils'
import { verifyApiSignature, isValidDateString } from './utils'
import { TestContainer, createTestApp } from '../tests/app'

describe('utils', (): void => {
Expand Down Expand Up @@ -145,4 +145,19 @@ describe('utils', (): void => {
expect(verified).toBe(false)
})
})

describe('isValidDateString', () => {
test.each([
['2024-12-05T15:10:09.545Z', true],
['2024-12-05', true],
['invalid-date', false], // Invalid date string
['2024-12-05T25:10:09.545Z', false], // Invalid date string (invalid hour)
['"2024-12-05T15:10:09.545Z"', false], // Improperly formatted string
['', false], // Empty string
[null, false], // Null value
[undefined, false] // Undefined value
])('should return %p for input %p', (input, expected) => {
expect(isValidDateString(input!)).toBe(expected)
})
})
})
5 changes: 5 additions & 0 deletions packages/auth/src/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,8 @@ export async function verifyApiSignature(

return verifyApiSignatureDigest(signature as string, ctx.request, config)
}

// Intended for Date strings like "2024-12-05T15:10:09.545Z" (e.g., from new Date().toISOString())
export function isValidDateString(date: string): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

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

small, but would be good to write a few tests for this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

return !isNaN(Date.parse(date))
}
Loading
Loading