diff --git a/app.ts b/app.ts deleted file mode 100644 index 63a57bf..0000000 --- a/app.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { feathers } from '@feathersjs/feathers' -import express, { - rest, - json, - urlencoded, - 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'; - -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'; - -const app: Application = express(feathers()) - -// Load app configuration -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 } -})); - -// Propagate session to request.params in feathers services -app.use(function (req, _res, next) { - req.feathers = { - ...req.feathers, - session: req.session - } - next() -}); - -app.use('/authme', (req, res) => { - //@ts-ignore - req.session.user = { - email: "her@va.mm" - } - res.send("done locally") -}) - -app.use(urlencoded({ extended: true })) -// Host the public folder -app.use('/', serveStatic(app.get('public'))) - -// Configure services and real-time functionality -app.configure(rest()) -app.configure( - socketio({ - cors: { - 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 })) - -// Register hooks that run on all service methods -app.hooks({ - around: { - all: [logError] - }, - before: {}, - after: {}, - error: {} -}) -// Register application setup and teardown hooks here -app.hooks({ - setup: [], - teardown: [] -}) - -export { app } diff --git a/channels.ts b/channels.ts deleted file mode 100644 index 9c72f46..0000000 --- a/channels.ts +++ /dev/null @@ -1,38 +0,0 @@ -// 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' - -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.' - ) - - app.on('connection', (connection: RealTimeConnection) => { - // On a new real-time connection, add it to the anonymous channel - 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) - - // 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) => { - // Here you can add event publishers to channels set up in `channels.js` - // 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') - }) -} diff --git a/clients/wildduck.client.ts b/clients/wildduck.client.ts deleted file mode 100644 index 383740c..0000000 --- a/clients/wildduck.client.ts +++ /dev/null @@ -1,12 +0,0 @@ -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', -}); - -export default wildDuckClient; diff --git a/declarations.ts b/declarations.ts deleted file mode 100644 index 781539e..0000000 --- a/declarations.ts +++ /dev/null @@ -1,20 +0,0 @@ -// 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 - -export { NextFunction } - -// The types for app.get(name) and app.set(name) -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface Configuration extends ApplicationConfiguration {} - -// A mapping of service names to types. Will be extended in service files. -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ServiceTypes {} - -// The application instance type that will be used everywhere else -export type Application = FeathersApplication - -// The context for hook functions - can be typed with a service class -export type HookContext = FeathersHookContext diff --git a/hooks/log-error.ts b/hooks/log-error.ts deleted file mode 100644 index 9a01e5f..0000000 --- a/hooks/log-error.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { HookContext, NextFunction } from '../declarations' -import { logger } from '../logger' - -export const logError = async (context: HookContext, next: NextFunction) => { - try { - await next() - } catch (error: any) { - logger.error(error.stack) - - // Log validation errors - if (error.data) { - logger.error('Data: %O', error.data) - } - - throw error - } -} diff --git a/hooks/validate-auth.ts b/hooks/validate-auth.ts deleted file mode 100644 index 98b2584..0000000 --- a/hooks/validate-auth.ts +++ /dev/null @@ -1,9 +0,0 @@ -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 diff --git a/public/index.html b/public/index.html index 05e989c..94f63a4 100644 --- a/public/index.html +++ b/public/index.html @@ -1,37 +1,45 @@ - - walias - - - - - - - - + body { + min-height: 100%; + display: flex; + align-items: center; + } + img.logo { + display: block; + margin: auto auto; + width: 30%; + max-width: 100%; + max-height: 100%; + } + + + + +
+ + + \ No newline at end of file diff --git a/public/index.js b/public/index.js new file mode 100644 index 0000000..4a45cec --- /dev/null +++ b/public/index.js @@ -0,0 +1,42 @@ +fetch('/aliases').then(async res => { + const data = await res.json() + const dataContainer = document.getElementById('container') + + if (!res.ok) { + // Show message "Login to see your aliases" and create a div with a button to login, url is /auth-oidc + dataContainer.innerHTML = ` + + Login + ` + + return + } + + dataContainer.innerHTML = ` + + + + + + + + + + + ${data.map(alias => ` + + + + + + `).join('')} + +
AddressCreatedActions
${alias.address}${alias.created} + Delete +
+ ` +}) \ No newline at end of file diff --git a/services/aliases/aliases.class.ts b/services/aliases/aliases.class.ts deleted file mode 100644 index 1ccbc6b..0000000 --- a/services/aliases/aliases.class.ts +++ /dev/null @@ -1,84 +0,0 @@ -import type { 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' - -interface Alias { - success: boolean, - id: string, - address: string, - main: boolean, - user: string, - tags: string[], - created: string, -} - -interface GetAddressInfoResponse { - success: boolean, - results: Alias[] -} - -interface CreateAddressResponse { - success: boolean, - id: string, -} - -type AliasesData = any -type AliasesPatch = any -type AliasesQuery = any - -export type { Alias as Aliases, AliasesData, AliasesPatch, AliasesQuery } - -export interface AliasesServiceOptions { - app: Application -} - -export interface AliasesParams extends Params { - session?: any -} - -export class AliasesService - implements ServiceInterface -{ - constructor(public options: AliasesServiceOptions) { } - - async find(params: ServiceParams): Promise { - const userId = await this.getUserIdByEmailAddress(params) - const { data: userAddressesResponse } = await wildDuckClient.get(`/users/${userId}/addresses`) - - return userAddressesResponse.results - } - - async create(data: AliasesData, params: ServiceParams): Promise - async create(data: AliasesData, params: ServiceParams): Promise { - const userId = await this.getUserIdByEmailAddress(params) - const alias = `${faker.animal.crocodilia().replace(/\s/, '').slice(10)}-${faker.git.commitSha({ length: 5 })}`; - - const createResult = await wildDuckClient.post(`/users/${userId}/addresses`, { - address: alias - }) - - if (!createResult.data.success) { - throw new BadRequest('Failed to create alias') - } - - const { data: userAddressesResponse } = await wildDuckClient.get(`/users/${userId}/addresses`) - - return userAddressesResponse.results - - } - - 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}`))) - - return addressInfoResponse.data.user - } -} - -export const getOptions = (app: Application) => { - return { app } -} diff --git a/services/aliases/aliases.ts b/services/aliases/aliases.ts deleted file mode 100644 index 2b9c3cf..0000000 --- a/services/aliases/aliases.ts +++ /dev/null @@ -1,41 +0,0 @@ -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 * from './aliases.class' - -export const aliases = (app: Application) => { - app.use(aliasesPath, new AliasesService(getOptions(app)), { - methods: aliasesMethods, - events: [] - }) - - app.service(aliasesPath).hooks({ - around: { - all: [] - }, - before: { - all: [ - validateAuth - ], - find: [], - create: [], - }, - after: { - all: [] - }, - error: { - all: [] - } - }) -} - -// Add this service to the service type index -declare module '../../declarations' { - interface ServiceTypes { - [aliasesPath]: AliasesService - } -} diff --git a/services/auth-oidc/auth-oidc.class.ts b/services/auth-oidc/auth-oidc.class.ts deleted file mode 100644 index 5a3fab1..0000000 --- a/services/auth-oidc/auth-oidc.class.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { Params, ServiceInterface } from '@feathersjs/feathers' - -import type { Application } from '../../declarations' - -import { Issuer, generators } from 'openid-client' -import config from 'config'; - - -type AuthOidcResponse = string -type AuthOidcQuery = any - -export type { AuthOidcResponse as AuthOidc, AuthOidcQuery } - -export interface AuthOidcServiceOptions { - app: Application -} - -export interface AuthOidcParams extends Params { - session?: any -} - -export class AuthOidcService - implements ServiceInterface -{ - constructor(public options: AuthOidcServiceOptions) { } - - async find(params: ServiceParams): Promise { - 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'], - }) - 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', - code_challenge: codeChallenge, - code_challenge_method: 'S256', - }); - - params.session.codeVerifier = codeVerifier; - return url; - } -} - -export const getOptions = (app: Application) => { - return { app } -} diff --git a/services/auth-oidc/auth-oidc.ts b/services/auth-oidc/auth-oidc.ts deleted file mode 100644 index e2a5a23..0000000 --- a/services/auth-oidc/auth-oidc.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { Application } from '../../declarations' -import { AuthOidcService, getOptions } from './auth-oidc.class' - -export const authOidcPath = 'auth-oidc' -export const authOidcMethods = ['find'] as const - -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.service(authOidcPath).hooks({ - around: { - all: [] - }, - before: { - all: [], - find: [], - }, - after: { - all: [] - }, - error: { - all: [] - } - }) -} - -declare module '../../declarations' { - interface ServiceTypes { - [authOidcPath]: AuthOidcService - } -} diff --git a/services/auth-oidc/callback/auth-oidc-callback.class.ts b/services/auth-oidc/callback/auth-oidc-callback.class.ts deleted file mode 100644 index abf1056..0000000 --- a/services/auth-oidc/callback/auth-oidc-callback.class.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { Params, ServiceInterface } from '@feathersjs/feathers' -import type { Application } from '../../../declarations' -import { Issuer } from 'openid-client' - -import config from 'config' - -type AuthOidcCallback = string -type AuthOidcCallbackData = any -type AuthOidcCallbackPatch = any -type AuthOidcCallbackQuery = any - -export type { AuthOidcCallback, AuthOidcCallbackData, AuthOidcCallbackPatch, AuthOidcCallbackQuery } - -export interface AuthOidcCallbackServiceOptions { - app: Application -} - -export interface AuthOidcCallbackParams extends Params { - session?: any - query: { - iss: string, - code: string, - } -} - -export class AuthOidcCallbackService - implements ServiceInterface -{ - constructor(public options: AuthOidcCallbackServiceOptions) { } - - async find(params: ServiceParams): Promise { - 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'], - }) - - 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 userinfo = await client.userinfo(tokenSet.access_token as string); - - params.session.user = userinfo; - - return '/' - } -} - -export const getOptions = (app: Application) => { - return { app } -} diff --git a/services/auth-oidc/callback/auth-oidc-callback.ts b/services/auth-oidc/callback/auth-oidc-callback.ts deleted file mode 100644 index 7037e23..0000000 --- a/services/auth-oidc/callback/auth-oidc-callback.ts +++ /dev/null @@ -1,42 +0,0 @@ -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 * 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.service(authOidcCallbackPath).hooks({ - around: { - all: [] - }, - before: { - all: [], - find: [], - }, - after: { - all: [] - }, - error: { - all: [] - } - }) -} - -declare module '../../../declarations' { - interface ServiceTypes { - [authOidcCallbackPath]: AuthOidcCallbackService - } -} diff --git a/services/index.ts b/services/index.ts deleted file mode 100644 index 04cc3a5..0000000 --- a/services/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -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) -} diff --git a/src/app.ts b/src/app.ts index 63a57bf..95a1f09 100644 --- a/src/app.ts +++ b/src/app.ts @@ -47,14 +47,6 @@ app.use(function (req, _res, next) { next() }); -app.use('/authme', (req, res) => { - //@ts-ignore - req.session.user = { - email: "her@va.mm" - } - res.send("done locally") -}) - app.use(urlencoded({ extended: true })) // Host the public folder app.use('/', serveStatic(app.get('public')))