add minimal html
This commit is contained in:
		
							
								
								
									
										93
									
								
								app.ts
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								app.ts
									
									
									
									
									
								
							| @@ -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 } | ||||
							
								
								
									
										38
									
								
								channels.ts
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								channels.ts
									
									
									
									
									
								
							| @@ -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') | ||||
|   }) | ||||
| } | ||||
| @@ -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; | ||||
| @@ -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<ServiceTypes, Configuration> | ||||
|  | ||||
| // The context for hook functions - can be typed with a service class | ||||
| export type HookContext<S = any> = FeathersHookContext<Application, S> | ||||
| @@ -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 | ||||
|   } | ||||
| } | ||||
| @@ -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') | ||||
|     } | ||||
| } | ||||
| @@ -1,37 +1,45 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <title>walias</title> | ||||
|     <meta name="description" content="Aliases for Wild Duck"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <style> | ||||
|       * { | ||||
|         margin: 0; | ||||
|         padding: 0; | ||||
|         box-sizing: border-box; | ||||
|       } | ||||
|  | ||||
|       html { | ||||
|         height: 100%; | ||||
|       } | ||||
| <head> | ||||
|   <title>walias</title> | ||||
|   <script src="index.js" async></script> | ||||
|   <meta name="description" content="Aliases for Wild Duck"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|   <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" | ||||
|     integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous"> | ||||
|   <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js" | ||||
|     integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" | ||||
|     crossorigin="anonymous"></script> | ||||
|   <style> | ||||
|     * { | ||||
|       margin: 0; | ||||
|       padding: 0; | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|  | ||||
|       body { | ||||
|         min-height: 100%; | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|       } | ||||
|     html { | ||||
|       height: 100%; | ||||
|     } | ||||
|  | ||||
|       img.logo { | ||||
|         display: block; | ||||
|         margin: auto auto; | ||||
|         width: 30%; | ||||
|         max-width: 100%; | ||||
|         max-height: 100%; | ||||
|       } | ||||
|     </style> | ||||
|   </head> | ||||
|   <body> | ||||
|     <img class="logo" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjUwMCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTEyOCA5LjEwMmM2NS42NjUgMCAxMTguODk4IDUzLjIzMyAxMTguODk4IDExOC44OTggMCA2NS42NjUtNTMuMjMzIDExOC44OTgtMTE4Ljg5OCAxMTguODk4QzYyLjMzNSAyNDYuODk4IDkuMTAyIDE5My42NjUgOS4xMDIgMTI4IDkuMTAyIDYyLjMzNSA2Mi4zMzUgOS4xMDIgMTI4IDkuMTAyTTEyOCAwQzU3LjQyMSAwIDAgNTcuNDIxIDAgMTI4YzAgNzAuNTc5IDU3LjQyMSAxMjggMTI4IDEyOCA3MC41NzkgMCAxMjgtNTcuNDIxIDEyOC0xMjhDMjU2IDU3LjQyMSAxOTguNTc5IDAgMTI4IDBtMjAuODMgMjUuNTI0Yy0xMC40My0xLjg5Ni0zNS42NTEgMzYuNDA5LTQzLjk5NCA1OS43MzQtLjYzNCAxLjc2OS0yLjA4NiA4LjI0OS0yLjA4NiA5Ljk1NSAwIDAgNi41MzEgMTQuMDU1IDguMzQzIDE3LjM1MS0zLjAzNC0xLjU4LTkuMzIzLTEzLjc1Ni05LjMyMy0xMy43NTYtMy4wMzQgNS43ODQtNS45NDIgMzIuMzQtNC45OTQgMzcuMjcxIDAgMCA2Ljc2MiAxMC4wNjIgOS4zODcgMTIuNTc4LTMuNjAzLTEuMjAxLTkuNjcxLTkuMzU1LTkuNjcxLTkuMzU1LTEuMTM4IDMuNTA4LS45MTYgMTAuODA3LS4zNzkgMTMuMjc0IDQuNTUxIDYuNjM3IDEwLjYxOSA3LjM5NiAxMC42MTkgNy4zOTZzLTYuNjM3IDY2LjE4MSAzLjQxMyA3MS4xMTFjNi4yNTgtMS4zMjcgNy43NzUtNzMuOTU2IDcuNzc1LTczLjk1NnM3LjU4NS41NjkgOS4yOTItMS4zMjdjMy44NTYtMi42NTUgMTIuODI2LTMwLjIyNCAxMi45NTgtMzQuMjAyIDAgMC0xMC40MSAxLjk1Mi0xNS40ODcgMy45MjQgMy44MjYtMy44IDE2LjA0OS02LjM1MiAxNi4wNDktNi4zNTIgMy4zMTUtMy45NzkgMTAuMjkxLTMxLjA0NyAxMC45OTQtMzkuMzkxLjE3Ni0yLjA5My41ODMtNC42NTcuMjY4LTguMzk4IDAgMC05Ljk0MSAyLjE3Ny0xMi4wMTQgMS40MjQgMi4xMDQtLjIzNyAxMi4yNjMtNC4xNCAxMi4yNjMtNC4xNCAxLjgwMS0xNi4yMTMgMi4zNTgtNDIuMDkxLTMuNDEzLTQzLjE0MXptLTM2LjM4IDE3MS42OTFjLS43OTUgMTkuNDk2LTEuMjk0IDI1LjAwNC0yLjExNSAyOS42MDEtLjM3OS44NTctLjc1OC45OTctMS4xMzgtLjA5NS0zLjQ3Ny0xNS45OTItMy4yMjQtMTM2LjQzOCAzNi40MDktMTkxLjI0MS0yMy4wNSA0Mi4wOTItMzMuNTM1IDEyMi44NjEtMzMuMTU2IDE2MS43MzV6IiBmaWxsPSIjMzMzIi8+PC9zdmc+" /> | ||||
|   </body> | ||||
| </html> | ||||
|     body { | ||||
|       min-height: 100%; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|     } | ||||
|  | ||||
|     img.logo { | ||||
|       display: block; | ||||
|       margin: auto auto; | ||||
|       width: 30%; | ||||
|       max-width: 100%; | ||||
|       max-height: 100%; | ||||
|     } | ||||
|   </style> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|   <div id="container"></div> | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
							
								
								
									
										42
									
								
								public/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								public/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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 = ` | ||||
|             <div class="alert alert-warning" role="alert"> | ||||
|                 Login to see your aliases | ||||
|             </div> | ||||
|             <a href="/auth-oidc" class="btn btn-primary">Login</a> | ||||
|         ` | ||||
|  | ||||
|         return | ||||
|     } | ||||
|  | ||||
|     dataContainer.innerHTML = ` | ||||
|         <div class="alert alert-success" role="alert"> | ||||
|             Your aliases | ||||
|         </div> | ||||
|         <table class="table"> | ||||
|             <thead> | ||||
|                 <tr> | ||||
|                     <th scope="col">Address</th>  | ||||
|                     <th scope="col">Created</th> | ||||
|                     <th scope="col">Actions</th> | ||||
|                 </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|                 ${data.map(alias => ` | ||||
|                     <tr> | ||||
|                         <td>${alias.address}</td> | ||||
|                         <td>${alias.created}</td> | ||||
|                         <td> | ||||
|                             <a href="/aliases/${alias.id}" class="btn btn-danger">Delete</a> | ||||
|                         </td> | ||||
|                     </tr> | ||||
|                 `).join('')} | ||||
|             </tbody> | ||||
|         </table> | ||||
|     ` | ||||
| }) | ||||
| @@ -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<AliasesQuery> { | ||||
|   session?: any | ||||
| } | ||||
|  | ||||
| export class AliasesService<ServiceParams extends AliasesParams = AliasesParams> | ||||
|   implements ServiceInterface<Alias, AliasesData, ServiceParams, AliasesPatch> | ||||
| { | ||||
|   constructor(public options: AliasesServiceOptions) { } | ||||
|  | ||||
|   async find(params: ServiceParams): Promise<Alias[]> { | ||||
|     const userId = await this.getUserIdByEmailAddress(params) | ||||
|     const { data: userAddressesResponse } = await wildDuckClient.get<GetAddressInfoResponse>(`/users/${userId}/addresses`) | ||||
|  | ||||
|     return userAddressesResponse.results | ||||
|   } | ||||
|  | ||||
|   async create(data: AliasesData, params: ServiceParams): Promise<Alias> | ||||
|   async create(data: AliasesData, params: ServiceParams): Promise<Alias | Alias[]> { | ||||
|     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<CreateAddressResponse>(`/users/${userId}/addresses`, { | ||||
|       address: alias | ||||
|     }) | ||||
|  | ||||
|     if (!createResult.data.success) { | ||||
|       throw new BadRequest('Failed to create alias') | ||||
|     } | ||||
|  | ||||
|     const { data: userAddressesResponse } = await wildDuckClient.get<GetAddressInfoResponse>(`/users/${userId}/addresses`) | ||||
|  | ||||
|     return userAddressesResponse.results | ||||
|  | ||||
|   } | ||||
|  | ||||
|   private async getUserIdByEmailAddress(params: ServiceParams): Promise<string> { | ||||
|     const emails = params.session?.user?.emails; | ||||
|  | ||||
|     const addressInfoResponse = await Promise.any(emails.map((email: string) =>  wildDuckClient.get<Alias>(`addresses/resolve/${email}`))) | ||||
|  | ||||
|     return addressInfoResponse.data.user | ||||
|   } | ||||
| } | ||||
|  | ||||
| export const getOptions = (app: Application) => { | ||||
|   return { app } | ||||
| } | ||||
| @@ -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 | ||||
|   } | ||||
| } | ||||
| @@ -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<AuthOidcQuery> { | ||||
|   session?: any | ||||
| } | ||||
|  | ||||
| export class AuthOidcService<ServiceParams extends AuthOidcParams = AuthOidcParams> | ||||
|   implements ServiceInterface<AuthOidcResponse, ServiceParams> | ||||
| { | ||||
|   constructor(public options: AuthOidcServiceOptions) { } | ||||
|  | ||||
|   async find(params: ServiceParams): Promise<AuthOidcResponse> { | ||||
|     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 } | ||||
| } | ||||
| @@ -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 | ||||
|   } | ||||
| } | ||||
| @@ -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<AuthOidcCallbackQuery> { | ||||
|   session?: any | ||||
|   query: { | ||||
|     iss: string, | ||||
|     code: string, | ||||
|   } | ||||
| } | ||||
|  | ||||
| export class AuthOidcCallbackService<ServiceParams extends AuthOidcCallbackParams = AuthOidcCallbackParams> | ||||
|   implements ServiceInterface<AuthOidcCallback, AuthOidcCallbackData, ServiceParams, AuthOidcCallbackPatch> | ||||
| { | ||||
|   constructor(public options: AuthOidcCallbackServiceOptions) { } | ||||
|  | ||||
|   async find(params: ServiceParams): Promise<AuthOidcCallback> { | ||||
|     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 } | ||||
| } | ||||
| @@ -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 | ||||
|   } | ||||
| } | ||||
| @@ -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) | ||||
| } | ||||
| @@ -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'))) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user