From 70d95be227112c1f2c22a71e9c2735cb36618f7a Mon Sep 17 00:00:00 2001 From: Sergo Date: Sun, 30 Jul 2023 00:50:42 +0300 Subject: [PATCH] add remove method for aliases --- index.ts | 16 ++- logger.ts | 8 +- src/app.ts | 94 ++++++------- src/channels.ts | 49 +++---- src/clients/wildduck.client.ts | 14 +- src/declarations.ts | 15 ++- src/hooks/log-error.ts | 14 +- src/hooks/validate-auth.ts | 12 +- src/index.ts | 16 ++- src/logger.ts | 8 +- src/services/aliases/aliases.class.ts | 127 +++++++++++------- src/services/aliases/aliases.ts | 36 +++-- src/services/auth-oidc/auth-oidc.class.ts | 50 +++---- src/services/auth-oidc/auth-oidc.ts | 44 +++--- .../callback/auth-oidc-callback.class.ts | 70 ++++++---- .../auth-oidc/callback/auth-oidc-callback.ts | 49 ++++--- src/services/index.ts | 16 +-- src/validators.ts | 42 +++--- test/app.test.ts | 52 +++---- test/services/aliases/aliases.test.ts | 16 +-- test/services/auth-oidc/auth-oidc.test.ts | 16 +-- .../auth-oidc/callback/callback.test.ts | 16 +-- validators.ts | 42 +++--- 23 files changed, 446 insertions(+), 376 deletions(-) diff --git a/index.ts b/index.ts index dc4ec62..a203c16 100644 --- a/index.ts +++ b/index.ts @@ -1,11 +1,13 @@ -import { app } from './app' -import { logger } from './logger' +import { app } from "./app"; +import { logger } from "./logger"; -const port = app.get('port') -const host = app.get('host') +const port = app.get("port"); +const host = app.get("host"); -process.on('unhandledRejection', (reason) => logger.error('Unhandled Rejection %O', reason)) +process.on("unhandledRejection", (reason) => + logger.error("Unhandled Rejection %O", reason), +); app.listen(port).then(() => { - logger.info(`Feathers app listening on http://${host}:${port}`) -}) + logger.info(`Feathers app listening on http://${host}:${port}`); +}); diff --git a/logger.ts b/logger.ts index 3280e9e..57edc25 100644 --- a/logger.ts +++ b/logger.ts @@ -1,10 +1,10 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html -import { createLogger, format, transports } from 'winston' +import { createLogger, format, transports } from "winston"; // Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston export const logger = createLogger({ // To see more detailed errors, change this to 'debug' - level: 'info', + level: "info", format: format.combine(format.splat(), format.simple()), - transports: [new transports.Console()] -}) + transports: [new transports.Console()], +}); diff --git a/src/app.ts b/src/app.ts index 95a1f09..f0a24a4 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,4 @@ -import { feathers } from '@feathersjs/feathers' +import { feathers } from "@feathersjs/feathers"; import express, { rest, json, @@ -6,80 +6,84 @@ import express, { cors, serveStatic, notFound, - errorHandler -} from '@feathersjs/express' -import configuration from '@feathersjs/configuration' -import socketio from '@feathersjs/socketio' -import session from 'express-session'; -import cookieParser from 'cookie-parser'; + errorHandler, +} from "@feathersjs/express"; +import configuration from "@feathersjs/configuration"; +import socketio from "@feathersjs/socketio"; +import session from "express-session"; +import cookieParser from "cookie-parser"; -import type { Application } from './declarations' +import type { Application } from "./declarations"; -import { logger } from './logger' -import { logError } from './hooks/log-error' -import { services } from './services/index' -import { channels } from './channels' -import { randomUUID } from 'crypto'; +import { logger } from "./logger"; +import { logError } from "./hooks/log-error"; +import { services } from "./services/index"; +import { channels } from "./channels"; +import { randomUUID } from "crypto"; -const app: Application = express(feathers()) +const app: Application = express(feathers()); // Load app configuration -app.configure(configuration()) -app.use(cors()) -app.use(json({ - limit: '20mb' -})) +app.configure(configuration()); +app.use(cors()); +app.use( + json({ + limit: "20mb", + }), +); app.use(cookieParser()); -app.use(session({ - secret: randomUUID(), - resave: false, - saveUninitialized: true, - cookie: { secure: false } -})); +app.use( + session({ + secret: randomUUID(), + resave: false, + saveUninitialized: true, + cookie: { secure: false }, + }), +); // Propagate session to request.params in feathers services app.use(function (req, _res, next) { req.feathers = { ...req.feathers, - session: req.session - } - next() + session: req.session, + }; + next(); }); -app.use(urlencoded({ extended: true })) +app.use(urlencoded({ extended: true })); // Host the public folder -app.use('/', serveStatic(app.get('public'))) +app.use("/", serveStatic(app.get("public"))); // Configure services and real-time functionality -app.configure(rest()) +app.configure(rest()); app.configure( socketio({ cors: { - origin: app.get('origins') - } - }) -) -app.configure(services) -app.configure(channels) + origin: app.get("origins"), + }, + }), +); +app.configure(services); +app.configure(channels); // Configure a middleware for 404s and the error handler -app.use(notFound()) -app.use(errorHandler({ logger })) +app.use(notFound()); +app.use(errorHandler({ logger })); // Register hooks that run on all service methods app.hooks({ around: { - all: [logError] + all: [logError], }, before: {}, after: {}, - error: {} -}) + error: {}, +}); // Register application setup and teardown hooks here app.hooks({ setup: [], - teardown: [] -}) + teardown: [], +}); -export { app } +export { app }; diff --git a/src/channels.ts b/src/channels.ts index 9c72f46..53d5203 100644 --- a/src/channels.ts +++ b/src/channels.ts @@ -1,31 +1,34 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/channels.html -import type { RealTimeConnection, Params } from '@feathersjs/feathers' -import type { AuthenticationResult } from '@feathersjs/authentication' -import '@feathersjs/transport-commons' -import type { Application, HookContext } from './declarations' -import { logger } from './logger' +import type { RealTimeConnection, Params } from "@feathersjs/feathers"; +import type { AuthenticationResult } from "@feathersjs/authentication"; +import "@feathersjs/transport-commons"; +import type { Application, HookContext } from "./declarations"; +import { logger } from "./logger"; export const channels = (app: Application) => { logger.warn( - 'Publishing all events to all authenticated users. See `channels.ts` and https://dove.feathersjs.com/api/channels.html for more information.' - ) + "Publishing all events to all authenticated users. See `channels.ts` and https://dove.feathersjs.com/api/channels.html for more information.", + ); - app.on('connection', (connection: RealTimeConnection) => { + app.on("connection", (connection: RealTimeConnection) => { // On a new real-time connection, add it to the anonymous channel - app.channel('anonymous').join(connection) - }) + app.channel("anonymous").join(connection); + }); - app.on('login', (authResult: AuthenticationResult, { connection }: Params) => { - // connection can be undefined if there is no - // real-time connection, e.g. when logging in via REST - if (connection) { - // The connection is no longer anonymous, remove it - app.channel('anonymous').leave(connection) + app.on( + "login", + (authResult: AuthenticationResult, { connection }: Params) => { + // connection can be undefined if there is no + // real-time connection, e.g. when logging in via REST + if (connection) { + // The connection is no longer anonymous, remove it + app.channel("anonymous").leave(connection); - // Add it to the authenticated user channel - app.channel('authenticated').join(connection) - } - }) + // Add it to the authenticated user channel + app.channel("authenticated").join(connection); + } + }, + ); // eslint-disable-next-line no-unused-vars app.publish((data: any, context: HookContext) => { @@ -33,6 +36,6 @@ export const channels = (app: Application) => { // To publish only for a specific event use `app.publish(eventname, () => {})` // e.g. to publish all service events to all authenticated users use - return app.channel('authenticated') - }) -} + return app.channel("authenticated"); + }); +}; diff --git a/src/clients/wildduck.client.ts b/src/clients/wildduck.client.ts index 383740c..ef880a2 100644 --- a/src/clients/wildduck.client.ts +++ b/src/clients/wildduck.client.ts @@ -1,12 +1,12 @@ -import axios from 'axios'; -import config from 'config'; +import axios from "axios"; +import config from "config"; const wildDuckClient = axios.create({ - baseURL: config.get('wildDuck.url'), - headers: { - 'X-Access-Token': config.get('wildDuck.token'), - }, - responseType: 'json', + baseURL: config.get("wildDuck.url"), + headers: { + "X-Access-Token": config.get("wildDuck.token"), + }, + responseType: "json", }); export default wildDuckClient; diff --git a/src/declarations.ts b/src/declarations.ts index 781539e..2f7319d 100644 --- a/src/declarations.ts +++ b/src/declarations.ts @@ -1,9 +1,12 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/typescript.html -import { HookContext as FeathersHookContext, NextFunction } from '@feathersjs/feathers' -import { Application as FeathersApplication } from '@feathersjs/express' -type ApplicationConfiguration = any +import { + HookContext as FeathersHookContext, + NextFunction, +} from "@feathersjs/feathers"; +import { Application as FeathersApplication } from "@feathersjs/express"; +type ApplicationConfiguration = any; -export { NextFunction } +export { NextFunction }; // The types for app.get(name) and app.set(name) // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -14,7 +17,7 @@ export interface Configuration extends ApplicationConfiguration {} export interface ServiceTypes {} // The application instance type that will be used everywhere else -export type Application = FeathersApplication +export type Application = FeathersApplication; // The context for hook functions - can be typed with a service class -export type HookContext = FeathersHookContext +export type HookContext = FeathersHookContext; diff --git a/src/hooks/log-error.ts b/src/hooks/log-error.ts index 9a01e5f..787730f 100644 --- a/src/hooks/log-error.ts +++ b/src/hooks/log-error.ts @@ -1,17 +1,17 @@ -import type { HookContext, NextFunction } from '../declarations' -import { logger } from '../logger' +import type { HookContext, NextFunction } from "../declarations"; +import { logger } from "../logger"; export const logError = async (context: HookContext, next: NextFunction) => { try { - await next() + await next(); } catch (error: any) { - logger.error(error.stack) + logger.error(error.stack); // Log validation errors if (error.data) { - logger.error('Data: %O', error.data) + logger.error("Data: %O", error.data); } - throw error + throw error; } -} +}; diff --git a/src/hooks/validate-auth.ts b/src/hooks/validate-auth.ts index 98b2584..3de2f11 100644 --- a/src/hooks/validate-auth.ts +++ b/src/hooks/validate-auth.ts @@ -1,9 +1,9 @@ -import { NotAuthenticated } from '@feathersjs/errors' -import type { HookContext, NextFunction } from '../declarations' +import { NotAuthenticated } from "@feathersjs/errors"; +import type { HookContext, NextFunction } from "../declarations"; // Check if user is stored in session export const validateAuth = async (context: HookContext) => { - if (!context.params.session?.user) { - throw new NotAuthenticated('Not authenticated') - } -} \ No newline at end of file + if (!context.params.session?.user) { + throw new NotAuthenticated("Not authenticated"); + } +}; diff --git a/src/index.ts b/src/index.ts index dc4ec62..a203c16 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,13 @@ -import { app } from './app' -import { logger } from './logger' +import { app } from "./app"; +import { logger } from "./logger"; -const port = app.get('port') -const host = app.get('host') +const port = app.get("port"); +const host = app.get("host"); -process.on('unhandledRejection', (reason) => logger.error('Unhandled Rejection %O', reason)) +process.on("unhandledRejection", (reason) => + logger.error("Unhandled Rejection %O", reason), +); app.listen(port).then(() => { - logger.info(`Feathers app listening on http://${host}:${port}`) -}) + logger.info(`Feathers app listening on http://${host}:${port}`); +}); diff --git a/src/logger.ts b/src/logger.ts index 3280e9e..57edc25 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,10 +1,10 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html -import { createLogger, format, transports } from 'winston' +import { createLogger, format, transports } from "winston"; // Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston export const logger = createLogger({ // To see more detailed errors, change this to 'debug' - level: 'info', + level: "info", format: format.combine(format.splat(), format.simple()), - transports: [new transports.Console()] -}) + transports: [new transports.Console()], +}); diff --git a/src/services/aliases/aliases.class.ts b/src/services/aliases/aliases.class.ts index 8af8a45..3013d2f 100644 --- a/src/services/aliases/aliases.class.ts +++ b/src/services/aliases/aliases.class.ts @@ -1,92 +1,123 @@ -import type { Params, ServiceInterface } from '@feathersjs/feathers' +import type { + NullableId, + Params, + ServiceInterface, +} from "@feathersjs/feathers"; -import type { Application } from '../../declarations' -import wildDuckClient from '../../clients/wildduck.client' -import { faker } from '@faker-js/faker' -import { BadRequest } from '@feathersjs/errors' -import config from 'config' +import type { Application } from "../../declarations"; +import wildDuckClient from "../../clients/wildduck.client"; +import { faker } from "@faker-js/faker"; +import { BadRequest } from "@feathersjs/errors"; +import config from "config"; interface Alias { - success: boolean, - id: string, - address: string, - main: boolean, - user: string, - tags: string[], - created: string, + success: boolean; + id: string; + address: string; + main: boolean; + user: string; + tags: string[]; + created: string; } interface GetAddressInfoResponse { - success: boolean, - results: Alias[] + success: boolean; + results: Alias[]; } interface CreateAddressResponse { - success: boolean, - id: string, + success: boolean; + id: string; } -type AliasesData = any -type AliasesPatch = any -type AliasesQuery = any +type AliasesData = any; +type AliasesPatch = any; +type AliasesQuery = any; -export type { Alias as Aliases, AliasesData, AliasesPatch, AliasesQuery } +export type { Alias as Aliases, AliasesData, AliasesPatch, AliasesQuery }; export interface AliasesServiceOptions { - app: Application + app: Application; } export interface AliasesParams extends Params { - session?: any + session?: any; } export class AliasesService implements ServiceInterface { - constructor(public options: AliasesServiceOptions) { } + constructor(public options: AliasesServiceOptions) {} async find(params: ServiceParams): Promise { - const userId = await this.getUserIdByEmailAddress(params) - const { data: userAddressesResponse } = await wildDuckClient.get(`/users/${userId}/addresses`) + const userId = await this.getUserIdByEmailAddress(params); - return userAddressesResponse.results + return this.getUserAddresses(userId); } - async create(data: AliasesData, params: ServiceParams): Promise - async create(data: AliasesData, params: ServiceParams): Promise { - const userId = await this.getUserIdByEmailAddress(params) - const aliasFirstPart = faker.animal.crocodilia() - .replace(/\D/g, '') - .replace(/\s/g, '') + async create(data: AliasesData, params: ServiceParams): Promise; + async create( + data: AliasesData, + params: ServiceParams, + ): Promise { + const userId = await this.getUserIdByEmailAddress(params); + const aliasFirstPart = faker.animal + .crocodilia() + .replace(/\D/g, "") + .replace(/\s/g, "") .slice(0, 10); const aliasSecondPart = faker.git.commitSha({ length: 5 }); - const alias = `${aliasFirstPart}-${aliasSecondPart}@${config.get('wildDuck.domain')}`; - // const alias = `${faker.animal.crocodilia().replace(/\s/, '').slice(10)}-${faker.git.commitSha({ length: 5 })}`; + const alias = `${aliasFirstPart}-${aliasSecondPart}@${config.get( + "wildDuck.domain", + )}`; - const createResult = await wildDuckClient.post(`/users/${userId}/addresses`, { - address: alias - }) + const createResult = await wildDuckClient.post( + `/users/${userId}/addresses`, + { + address: alias, + }, + ); if (!createResult.data.success) { - throw new BadRequest('Failed to create alias') + throw new BadRequest("Failed to create alias"); } - const { data: userAddressesResponse } = await wildDuckClient.get(`/users/${userId}/addresses`) - - return userAddressesResponse.results - + return this.getUserAddresses(userId); } - private async getUserIdByEmailAddress(params: ServiceParams): Promise { + private async getUserIdByEmailAddress( + params: ServiceParams, + ): Promise { const emails = params.session?.user?.emails; - const addressInfoResponse = await Promise.any(emails.map((email: string) => wildDuckClient.get(`addresses/resolve/${email}`))) + const addressInfoResponse = await Promise.any( + emails.map((email: string) => + wildDuckClient.get(`addresses/resolve/${email}`), + ), + ); - return addressInfoResponse.data.user + return addressInfoResponse.data.user; + } + + private async getUserAddresses(userId: string): Promise { + const { data: userAddressesResponse } = + await wildDuckClient.get( + `/users/${userId}/addresses`, + ); + + return userAddressesResponse.results; + } + + async remove(id: NullableId, params: ServiceParams): Promise { + await wildDuckClient.delete(`/addresses/${id}`); + + const userId = await this.getUserIdByEmailAddress(params); + + return this.getUserAddresses(userId); } } export const getOptions = (app: Application) => { - return { app } -} + return { app }; +}; diff --git a/src/services/aliases/aliases.ts b/src/services/aliases/aliases.ts index 2b9c3cf..0a6ed37 100644 --- a/src/services/aliases/aliases.ts +++ b/src/services/aliases/aliases.ts @@ -1,41 +1,39 @@ -import type { Application } from '../../declarations' -import { validateAuth } from '../../hooks/validate-auth' -import { AliasesService, getOptions } from './aliases.class' +import type { Application } from "../../declarations"; +import { validateAuth } from "../../hooks/validate-auth"; +import { AliasesService, getOptions } from "./aliases.class"; -export const aliasesPath = 'aliases' -export const aliasesMethods = ['find', 'create'] as const +export const aliasesPath = "aliases"; +export const aliasesMethods = ["find", "create"] as const; -export * from './aliases.class' +export * from "./aliases.class"; export const aliases = (app: Application) => { app.use(aliasesPath, new AliasesService(getOptions(app)), { methods: aliasesMethods, - events: [] - }) + events: [], + }); app.service(aliasesPath).hooks({ around: { - all: [] + all: [], }, before: { - all: [ - validateAuth - ], + all: [validateAuth], find: [], create: [], }, after: { - all: [] + all: [], }, error: { - all: [] - } - }) -} + all: [], + }, + }); +}; // Add this service to the service type index -declare module '../../declarations' { +declare module "../../declarations" { interface ServiceTypes { - [aliasesPath]: AliasesService + [aliasesPath]: AliasesService; } } diff --git a/src/services/auth-oidc/auth-oidc.class.ts b/src/services/auth-oidc/auth-oidc.class.ts index 5a3fab1..4a5797f 100644 --- a/src/services/auth-oidc/auth-oidc.class.ts +++ b/src/services/auth-oidc/auth-oidc.class.ts @@ -1,46 +1,46 @@ -import type { Params, ServiceInterface } from '@feathersjs/feathers' +import type { Params, ServiceInterface } from "@feathersjs/feathers"; -import type { Application } from '../../declarations' +import type { Application } from "../../declarations"; -import { Issuer, generators } from 'openid-client' -import config from 'config'; +import { Issuer, generators } from "openid-client"; +import config from "config"; +type AuthOidcResponse = string; +type AuthOidcQuery = any; -type AuthOidcResponse = string -type AuthOidcQuery = any - -export type { AuthOidcResponse as AuthOidc, AuthOidcQuery } +export type { AuthOidcResponse as AuthOidc, AuthOidcQuery }; export interface AuthOidcServiceOptions { - app: Application + app: Application; } export interface AuthOidcParams extends Params { - session?: any + session?: any; } -export class AuthOidcService - implements ServiceInterface +export class AuthOidcService< + ServiceParams extends AuthOidcParams = AuthOidcParams, +> implements ServiceInterface { - constructor(public options: AuthOidcServiceOptions) { } + constructor(public options: AuthOidcServiceOptions) {} async find(params: ServiceParams): Promise { - const issuer = await Issuer.discover(config.get('oidc.gatewayUri')); + const issuer = await Issuer.discover(config.get("oidc.gatewayUri")); const client = new issuer.Client({ - client_id: config.get('oidc.clientId'), - client_secret: config.get('oidc.clientSecret'), - redirect_uris: [config.get('oidc.redirectUris')], - response_types: ['code'], - }) + client_id: config.get("oidc.clientId"), + client_secret: config.get("oidc.clientSecret"), + redirect_uris: [config.get("oidc.redirectUris")], + response_types: ["code"], + }); const codeVerifier = generators.codeVerifier(); const codeChallenge = generators.codeChallenge(codeVerifier); const url = client.authorizationUrl({ - redirect_uri: config.get('clientUrl') + '/auth-oidc/callback', - scope: 'openid profile offline_access', - response_type: 'code', + redirect_uri: config.get("clientUrl") + "/auth-oidc/callback", + scope: "openid profile offline_access", + response_type: "code", code_challenge: codeChallenge, - code_challenge_method: 'S256', + code_challenge_method: "S256", }); params.session.codeVerifier = codeVerifier; @@ -49,5 +49,5 @@ export class AuthOidcService { - return { app } -} + return { app }; +}; diff --git a/src/services/auth-oidc/auth-oidc.ts b/src/services/auth-oidc/auth-oidc.ts index e2a5a23..fc4c484 100644 --- a/src/services/auth-oidc/auth-oidc.ts +++ b/src/services/auth-oidc/auth-oidc.ts @@ -1,41 +1,45 @@ -import type { Application } from '../../declarations' -import { AuthOidcService, getOptions } from './auth-oidc.class' +import type { Application } from "../../declarations"; +import { AuthOidcService, getOptions } from "./auth-oidc.class"; -export const authOidcPath = 'auth-oidc' -export const authOidcMethods = ['find'] as const +export const authOidcPath = "auth-oidc"; +export const authOidcMethods = ["find"] as const; -export * from './auth-oidc.class' +export * from "./auth-oidc.class"; export const authOidc = (app: Application) => { // TODO: fix this to use the correct type // @ts-ignore - app.use(authOidcPath, new AuthOidcService(getOptions(app)), { - methods: authOidcMethods, - events: [] - }, (req: any, res: any) => { - - return res.redirect(res.data); - }) + app.use( + authOidcPath, + new AuthOidcService(getOptions(app)), + { + methods: authOidcMethods, + events: [], + }, + (req: any, res: any) => { + return res.redirect(res.data); + }, + ); app.service(authOidcPath).hooks({ around: { - all: [] + all: [], }, before: { all: [], find: [], }, after: { - all: [] + all: [], }, error: { - all: [] - } - }) -} + all: [], + }, + }); +}; -declare module '../../declarations' { +declare module "../../declarations" { interface ServiceTypes { - [authOidcPath]: AuthOidcService + [authOidcPath]: AuthOidcService; } } diff --git a/src/services/auth-oidc/callback/auth-oidc-callback.class.ts b/src/services/auth-oidc/callback/auth-oidc-callback.class.ts index abf1056..cd4e3db 100644 --- a/src/services/auth-oidc/callback/auth-oidc-callback.class.ts +++ b/src/services/auth-oidc/callback/auth-oidc-callback.class.ts @@ -1,52 +1,68 @@ -import type { Params, ServiceInterface } from '@feathersjs/feathers' -import type { Application } from '../../../declarations' -import { Issuer } from 'openid-client' +import type { Params, ServiceInterface } from "@feathersjs/feathers"; +import type { Application } from "../../../declarations"; +import { Issuer } from "openid-client"; -import config from 'config' +import config from "config"; -type AuthOidcCallback = string -type AuthOidcCallbackData = any -type AuthOidcCallbackPatch = any -type AuthOidcCallbackQuery = any +type AuthOidcCallback = string; +type AuthOidcCallbackData = any; +type AuthOidcCallbackPatch = any; +type AuthOidcCallbackQuery = any; -export type { AuthOidcCallback, AuthOidcCallbackData, AuthOidcCallbackPatch, AuthOidcCallbackQuery } +export type { + AuthOidcCallback, + AuthOidcCallbackData, + AuthOidcCallbackPatch, + AuthOidcCallbackQuery, +}; export interface AuthOidcCallbackServiceOptions { - app: Application + app: Application; } export interface AuthOidcCallbackParams extends Params { - session?: any + session?: any; query: { - iss: string, - code: string, - } + iss: string; + code: string; + }; } -export class AuthOidcCallbackService - implements ServiceInterface +export class AuthOidcCallbackService< + ServiceParams extends AuthOidcCallbackParams = AuthOidcCallbackParams, +> implements + ServiceInterface< + AuthOidcCallback, + AuthOidcCallbackData, + ServiceParams, + AuthOidcCallbackPatch + > { - constructor(public options: AuthOidcCallbackServiceOptions) { } + constructor(public options: AuthOidcCallbackServiceOptions) {} async find(params: ServiceParams): Promise { - const issuer = await Issuer.discover(config.get('oidc.gatewayUri')); + const issuer = await Issuer.discover(config.get("oidc.gatewayUri")); const client = new issuer.Client({ - client_id: config.get('oidc.clientId'), - client_secret: config.get('oidc.clientSecret'), - redirect_uris: [config.get('oidc.redirectUris')], - response_types: ['code'], - }) + client_id: config.get("oidc.clientId"), + client_secret: config.get("oidc.clientSecret"), + redirect_uris: [config.get("oidc.redirectUris")], + response_types: ["code"], + }); const codeVerifier = params.session.codeVerifier; - const tokenSet = await client.callback(config.get('clientUrl') + '/auth-oidc/callback', { code: params.query.code, iss: params.query.iss }, { code_verifier: codeVerifier }); + const tokenSet = await client.callback( + config.get("clientUrl") + "/auth-oidc/callback", + { code: params.query.code, iss: params.query.iss }, + { code_verifier: codeVerifier }, + ); const userinfo = await client.userinfo(tokenSet.access_token as string); params.session.user = userinfo; - return '/' + return "/"; } } export const getOptions = (app: Application) => { - return { app } -} + return { app }; +}; diff --git a/src/services/auth-oidc/callback/auth-oidc-callback.ts b/src/services/auth-oidc/callback/auth-oidc-callback.ts index 7037e23..5131b2e 100644 --- a/src/services/auth-oidc/callback/auth-oidc-callback.ts +++ b/src/services/auth-oidc/callback/auth-oidc-callback.ts @@ -1,42 +1,49 @@ -import { http } from '@feathersjs/transport-commons' -import type { Application } from '../../../declarations' -import { AuthOidcCallbackService, getOptions } from './auth-oidc-callback.class' +import { http } from "@feathersjs/transport-commons"; +import type { Application } from "../../../declarations"; +import { + AuthOidcCallbackService, + getOptions, +} from "./auth-oidc-callback.class"; -export const authOidcCallbackPath = 'auth-oidc/callback' -export const authOidcCallbackMethods = ['find'] as const +export const authOidcCallbackPath = "auth-oidc/callback"; +export const authOidcCallbackMethods = ["find"] as const; -export * from './auth-oidc-callback.class' +export * from "./auth-oidc-callback.class"; export const authOidcCallback = (app: Application) => { // TODO: fix this to use the correct type // @ts-ignore - app.use(authOidcCallbackPath, new AuthOidcCallbackService(getOptions(app)), { - methods: authOidcCallbackMethods, - events: [] - }, (req: any, res: any) => { - - return res.redirect(res.data); - }) + app.use( + authOidcCallbackPath, + new AuthOidcCallbackService(getOptions(app)), + { + methods: authOidcCallbackMethods, + events: [], + }, + (req: any, res: any) => { + return res.redirect(res.data); + }, + ); app.service(authOidcCallbackPath).hooks({ around: { - all: [] + all: [], }, before: { all: [], find: [], }, after: { - all: [] + all: [], }, error: { - all: [] - } - }) -} + all: [], + }, + }); +}; -declare module '../../../declarations' { +declare module "../../../declarations" { interface ServiceTypes { - [authOidcCallbackPath]: AuthOidcCallbackService + [authOidcCallbackPath]: AuthOidcCallbackService; } } diff --git a/src/services/index.ts b/src/services/index.ts index 04cc3a5..b5e6904 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,10 +1,10 @@ -import { authOidcCallback } from './auth-oidc/callback/auth-oidc-callback' -import { authOidc } from './auth-oidc/auth-oidc' -import { aliases } from './aliases/aliases' -import type { Application } from '../declarations' +import { authOidcCallback } from "./auth-oidc/callback/auth-oidc-callback"; +import { authOidc } from "./auth-oidc/auth-oidc"; +import { aliases } from "./aliases/aliases"; +import type { Application } from "../declarations"; export const services = (app: Application) => { - app.configure(authOidcCallback) - app.configure(authOidc) - app.configure(aliases) -} + app.configure(authOidcCallback); + app.configure(authOidc); + app.configure(aliases); +}; diff --git a/src/validators.ts b/src/validators.ts index 015c65f..a540fc9 100644 --- a/src/validators.ts +++ b/src/validators.ts @@ -1,29 +1,29 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/validators.html -import { Ajv, addFormats } from '@feathersjs/schema' -import type { FormatsPluginOptions } from '@feathersjs/schema' +import { Ajv, addFormats } from "@feathersjs/schema"; +import type { FormatsPluginOptions } from "@feathersjs/schema"; const formats: FormatsPluginOptions = [ - 'date-time', - 'time', - 'date', - 'email', - 'hostname', - 'ipv4', - 'ipv6', - 'uri', - 'uri-reference', - 'uuid', - 'uri-template', - 'json-pointer', - 'relative-json-pointer', - 'regex' -] + "date-time", + "time", + "date", + "email", + "hostname", + "ipv4", + "ipv6", + "uri", + "uri-reference", + "uuid", + "uri-template", + "json-pointer", + "relative-json-pointer", + "regex", +]; -export const dataValidator: Ajv = addFormats(new Ajv({}), formats) +export const dataValidator: Ajv = addFormats(new Ajv({}), formats); export const queryValidator: Ajv = addFormats( new Ajv({ - coerceTypes: true + coerceTypes: true, }), - formats -) + formats, +); diff --git a/test/app.test.ts b/test/app.test.ts index 0ac9848..fb37c1b 100644 --- a/test/app.test.ts +++ b/test/app.test.ts @@ -1,40 +1,40 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/app.test.html -import assert from 'assert' -import axios from 'axios' -import type { Server } from 'http' -import { app } from '../src/app' +import assert from "assert"; +import axios from "axios"; +import type { Server } from "http"; +import { app } from "../src/app"; -const port = app.get('port') -const appUrl = `http://${app.get('host')}:${port}` +const port = app.get("port"); +const appUrl = `http://${app.get("host")}:${port}`; -describe('Feathers application tests', () => { - let server: Server +describe("Feathers application tests", () => { + let server: Server; before(async () => { - server = await app.listen(port) - }) + server = await app.listen(port); + }); after(async () => { - await app.teardown() - }) + await app.teardown(); + }); - it('starts and shows the index page', async () => { - const { data } = await axios.get(appUrl) + it("starts and shows the index page", async () => { + const { data } = await axios.get(appUrl); - assert.ok(data.indexOf('') !== -1) - }) + assert.ok(data.indexOf('') !== -1); + }); - it('shows a 404 JSON error', async () => { + it("shows a 404 JSON error", async () => { try { await axios.get(`${appUrl}/path/to/nowhere`, { - responseType: 'json' - }) - assert.fail('should never get here') + responseType: "json", + }); + assert.fail("should never get here"); } catch (error: any) { - const { response } = error - assert.strictEqual(response?.status, 404) - assert.strictEqual(response?.data?.code, 404) - assert.strictEqual(response?.data?.name, 'NotFound') + const { response } = error; + assert.strictEqual(response?.status, 404); + assert.strictEqual(response?.data?.code, 404); + assert.strictEqual(response?.data?.name, "NotFound"); } - }) -}) + }); +}); diff --git a/test/services/aliases/aliases.test.ts b/test/services/aliases/aliases.test.ts index e7f9b01..db9f5e2 100644 --- a/test/services/aliases/aliases.test.ts +++ b/test/services/aliases/aliases.test.ts @@ -1,11 +1,11 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html -import assert from 'assert' -import { app } from '../../../src/app' +import assert from "assert"; +import { app } from "../../../src/app"; -describe('aliases service', () => { - it('registered the service', () => { - const service = app.service('aliases') +describe("aliases service", () => { + it("registered the service", () => { + const service = app.service("aliases"); - assert.ok(service, 'Registered the service') - }) -}) + assert.ok(service, "Registered the service"); + }); +}); diff --git a/test/services/auth-oidc/auth-oidc.test.ts b/test/services/auth-oidc/auth-oidc.test.ts index 3731eb5..e877a96 100644 --- a/test/services/auth-oidc/auth-oidc.test.ts +++ b/test/services/auth-oidc/auth-oidc.test.ts @@ -1,11 +1,11 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html -import assert from 'assert' -import { app } from '../../../src/app' +import assert from "assert"; +import { app } from "../../../src/app"; -describe('auth-oidc service', () => { - it('registered the service', () => { - const service = app.service('auth-oidc') +describe("auth-oidc service", () => { + it("registered the service", () => { + const service = app.service("auth-oidc"); - assert.ok(service, 'Registered the service') - }) -}) + assert.ok(service, "Registered the service"); + }); +}); diff --git a/test/services/auth-oidc/callback/callback.test.ts b/test/services/auth-oidc/callback/callback.test.ts index 154cbc3..2e53041 100644 --- a/test/services/auth-oidc/callback/callback.test.ts +++ b/test/services/auth-oidc/callback/callback.test.ts @@ -1,11 +1,11 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html -import assert from 'assert' -import { app } from '../../../../src/app' +import assert from "assert"; +import { app } from "../../../../src/app"; -describe('auth-oidc/callback service', () => { - it('registered the service', () => { - const service = app.service('auth-oidc/callback') +describe("auth-oidc/callback service", () => { + it("registered the service", () => { + const service = app.service("auth-oidc/callback"); - assert.ok(service, 'Registered the service') - }) -}) + assert.ok(service, "Registered the service"); + }); +}); diff --git a/validators.ts b/validators.ts index 015c65f..a540fc9 100644 --- a/validators.ts +++ b/validators.ts @@ -1,29 +1,29 @@ // For more information about this file see https://dove.feathersjs.com/guides/cli/validators.html -import { Ajv, addFormats } from '@feathersjs/schema' -import type { FormatsPluginOptions } from '@feathersjs/schema' +import { Ajv, addFormats } from "@feathersjs/schema"; +import type { FormatsPluginOptions } from "@feathersjs/schema"; const formats: FormatsPluginOptions = [ - 'date-time', - 'time', - 'date', - 'email', - 'hostname', - 'ipv4', - 'ipv6', - 'uri', - 'uri-reference', - 'uuid', - 'uri-template', - 'json-pointer', - 'relative-json-pointer', - 'regex' -] + "date-time", + "time", + "date", + "email", + "hostname", + "ipv4", + "ipv6", + "uri", + "uri-reference", + "uuid", + "uri-template", + "json-pointer", + "relative-json-pointer", + "regex", +]; -export const dataValidator: Ajv = addFormats(new Ajv({}), formats) +export const dataValidator: Ajv = addFormats(new Ajv({}), formats); export const queryValidator: Ajv = addFormats( new Ajv({ - coerceTypes: true + coerceTypes: true, }), - formats -) + formats, +);