add minimal html
This commit is contained in:
parent
27efd26d6c
commit
88e1f756e3
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>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<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 {
|
<head>
|
||||||
height: 100%;
|
<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 {
|
html {
|
||||||
min-height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
}
|
||||||
align-items: center;
|
|
||||||
}
|
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>
|
||||||
|
|
||||||
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>
|
</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()
|
next()
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use('/authme', (req, res) => {
|
|
||||||
//@ts-ignore
|
|
||||||
req.session.user = {
|
|
||||||
email: "her@va.mm"
|
|
||||||
}
|
|
||||||
res.send("done locally")
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(urlencoded({ extended: true }))
|
app.use(urlencoded({ extended: true }))
|
||||||
// Host the public folder
|
// Host the public folder
|
||||||
app.use('/', serveStatic(app.get('public')))
|
app.use('/', serveStatic(app.get('public')))
|
||||||
|
Loading…
Reference in New Issue
Block a user