Skip to content

Commit

Permalink
feat: Add optional static bearer auth builder, with hashed tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
nklomp committed Aug 6, 2023
1 parent 960f2df commit 6a7dd17
Showing 1 changed file with 151 additions and 0 deletions.
151 changes: 151 additions & 0 deletions packages/express-support/src/static-bearer-auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import passport from 'passport'
import * as u8a from 'uint8arrays'
import { BearerUser, IStaticBearerVerifyOptions } from './types'
export class StaticBearerAuth {
private readonly strategy: string
private static providers: Map<string, StaticBearerUserProvider> = new Map()
private static verifyOptions: Map<string, IStaticBearerVerifyOptions | string> = new Map()
private hashTokens?: boolean = false

public static init(strategy: string, provider?: StaticBearerUserProvider) {
return new StaticBearerAuth(strategy ?? 'bearer', provider ?? new MapBasedStaticBearerUserProvider(strategy))
}

private constructor(strategy: string, provider: StaticBearerUserProvider) {
this.strategy = strategy
if (StaticBearerAuth.providers.has(strategy)) {
if (StaticBearerAuth.providers.get(strategy) !== provider) {
throw Error('Cannot register another user provider for strategy: ' + strategy)
}
} else {
StaticBearerAuth.providers.set(strategy, provider)
}
}

get provider() {
const provider = StaticBearerAuth.providers.get(this.strategy)
if (!provider) {
throw Error('Could not get user provider for ' + this.strategy)
}
return provider
}

withHashTokens(hashTokens: boolean): this {
this.hashTokens = hashTokens
return this
}

withUsers(users: BearerUser[] | BearerUser): this {
this.addUser(users)
return this
}

addUser(user: BearerUser[] | BearerUser): this {
this.provider.addUser(user)
return this
}

withVerifyOptions(options: IStaticBearerVerifyOptions | string): this {
StaticBearerAuth.verifyOptions.set(this.strategy, options)
return this
}

connectPassport() {
const _provider = this.provider
function findUser(token: string, cb: (error: any, user: any, options?: IStaticBearerVerifyOptions | string) => void) {
const user = _provider.getUser(token)
if (user) {
return cb(null, user)
}
return cb('bearer token not found or incorrect', false)
}

import('passport-http-bearer')
.then((httpBearer) => {
const hashTokens = this.hashTokens ?? false
passport.use(
this.strategy,
new httpBearer.Strategy({ passReqToCallback: false }, function (
token: string,
cb: (error: any, user: any, options?: IStaticBearerVerifyOptions | string) => void
): void {
if (hashTokens) {
import('@noble/hashes/sha256')
.then((hash) => {
findUser(u8a.toString(hash.sha256(token)), cb)
})
.catch((error) => {
console.log(error)
throw Error('Did you include @noble/hashes in package.json?')
})
} else {
findUser(token, cb)
}
})
)
})
.catch((error) => {
console.log(error)
throw Error('Did you include passport-http-bearer in package.json?')
})
}
}

export interface StaticBearerUserProvider {
strategy: string

addUser(user: BearerUser | BearerUser[], hashToken?: boolean): void

getUser(token: string): BearerUser | undefined

hashedTokens?: boolean
}

export class MapBasedStaticBearerUserProvider implements StaticBearerUserProvider {
private readonly _strategy: string
private readonly _users: BearerUser[] = []
private readonly _hashedTokens: boolean

constructor(strategy: string, hashedTokens?: boolean) {
this._strategy = strategy
this._hashedTokens = hashedTokens ?? false
}

get users(): BearerUser[] {
return this._users
}

get hashedTokens(): boolean {
return this._hashedTokens
}

get strategy(): string {
return this._strategy
}

getUser(token: string): BearerUser | undefined {
return this.users.find((user) => user.token === token)
}

addUser(user: BearerUser | BearerUser[], hashToken?: boolean): void {
const users = Array.isArray(user) ? user : [user]
if (hashToken) {
if (!this.hashedTokens) {
throw Error('Cannot hash token, when hashed tokens is not enabled on the user provider for strategy ' + this.strategy)
}
import('@noble/hashes/sha256')
.then((hash) => {
users.forEach((user) => (user.token = u8a.toString(hash.sha256(user.token))))
})
.catch((error) => {
console.log(error)
throw Error('Did you include @noble/hashes in package.json?')
})
}
this._users.push(...users)
}

getUsers(): BearerUser[] {
return this._users
}
}

0 comments on commit 6a7dd17

Please sign in to comment.