src/auth/Authenticator.ts
import {
IAuthenticator,
AuthSession,
Session,
ISession,
User,
GitHubProvider,
VercelProvider,
Dir,
IDir,
AccessTokenType,
AuthBrowserType,
IAuthProvider,
} from '..'
// import browserSync, { BrowserSyncInstance } from 'browser-sync'
import passport from 'passport'
import express, { Express } from 'express'
import bodyParser from 'body-parser'
import cors from 'cors'
import cookieParser from 'cookie-parser'
import http from 'http'
import path from 'path'
/**
*
*/
export class Authenticator implements IAuthenticator {
/** */
public static AUTH_PORT: number = 13013
/** */
public static AUTH_HOST: string = '127.0.0.1'
/** */
public static AUTH_PROTOCOL: string = 'http'
/** @internal */
protected _session: ISession
/** @internal */
protected _port: number
/** @internal */
protected _host: string
/** @internal */
protected _protocol: string
/** @internal */
protected _dir: IDir
/** @internal */
protected _app: Express
// /** @internal */
// protected _browser: AuthBrowserType
/** @internal */
protected _providers: Map<AccessTokenType, IAuthProvider>
/** @internal */
protected _user?: User
/**
*
* @param session
*/
constructor(session: ISession) {
this._session = session
this._port = Authenticator.AUTH_PORT
this._host = Authenticator.AUTH_HOST
this._protocol = Authenticator.AUTH_PROTOCOL
this._dir = new Dir(path.resolve(__dirname, '../../auth'))
this._app = express()
// this._browser = browserSync.create()
this._providers = new Map<AccessTokenType, IAuthProvider>()
}
/**
*
*/
get user() {
return this._user
}
/**
*
*/
get session() {
return this._session
}
/**
*
*/
get providers() {
return this._providers
}
/**
*
*/
get app() {
return this._app
}
// /**
// *
// */
// get browser() {
// return this._browser
// }
/**
*
*/
get port() {
return this._port
}
/**
*
*/
get host() {
return this._host
}
/**
*
*/
get dir() {
return this._dir
}
/**
*
*/
get protocol() {
return this._protocol
}
/**
*
*/
get baseUrl() {
return `${this.protocol}://${this.host}:${this.port}`
}
/**
*
* @param uri
*/
endpoint(uri: string) {
return `${this.baseUrl}/${uri}`
}
/**
*
*/
async openBrowser() {
// this.browser.init({
// startPath: '/',
// notify: true,
// logLevel: 'silent',
// ui: false,
// host: this.host,
// proxy: this.baseUrl,
// })
}
/** @internal */
async _initializeApp() {
this.app.set('port', this.port)
this.app.set('views', this.dir.path!)
this.app.set('view engine', 'ejs')
this.app.use(
cors({
origin: this.baseUrl,
methods: ['GET', 'POST'],
credentials: true,
})
)
this.app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', this.baseUrl)
res.header('Access-Control-Allow-Credentials', 'true')
next()
})
this.app.use(bodyParser.json())
this.app.use(bodyParser.urlencoded({ extended: false }))
this.app.use(cookieParser())
this.app.use(express.static(this.dir.path!))
this.app.use(
AuthSession({
name: this.session.name,
secret: this.session.id,
resave: true,
saveUninitialized: false,
cookie: { maxAge: Session.DEFAULT_EXPIRATION },
store: this.session.store,
})
)
this.app.use(passport.initialize())
this.app.use(passport.session())
}
/**
*
*/
async stop(when: number) {
setTimeout(() => {
// this.browser.exit()
}, when)
}
/**
*
* @param user
*/
update(user: User) {
this._user = user
}
/**
*
*/
async initialize() {
this._initializeApp()
this.app.get('/', (req, res) => {
if (!req.isAuthenticated()) {
res.redirect('/auth/login')
return
}
res.render('screens/main', {
user: req.user,
carmel: {
version: this.session.pkg.version,
},
})
this.stop(2000)
})
this.app.get('/auth/login', (req, res) => {
if (req.isAuthenticated()) {
res.redirect('/')
return
}
res.render('screens/auth', {
carmel: {
version: this.session.pkg.version,
},
})
})
this.app.get('/auth/logout', (req: any, res) => {
req.logOut()
req.session.destroy(() => {
res.clearCookie(this.session.name)
res.redirect('/')
})
})
const githubProvider = new GitHubProvider(this)
await githubProvider.initialize()
await githubProvider.prepareKeys()
const vercelProvider = new VercelProvider(this)
await vercelProvider.initialize()
await vercelProvider.prepareKeys()
// Add providers
this.providers.set(AccessTokenType.GITHUB, githubProvider)
this.providers.set(AccessTokenType.VERCEL, vercelProvider)
}
/**
*
*/
async setupSecurity() {
await Promise.all(
Array.from(this.providers.values()).map((provider) =>
provider.prepareKeys()
)
)
}
/**
*
*/
async start() {
const serverInstance = new http.Server(this.app)
return new Promise((resolve, reject) => {
// this.browser.emitter.on('service:exit', () => resolve())
serverInstance.listen(this.port, async () => {
this.openBrowser()
})
})
}
}