Skip to content

Commit

Permalink
http basic authentication support #57
Browse files Browse the repository at this point in the history
  • Loading branch information
rejetto committed Jun 26, 2022
1 parent f356d18 commit ef589f2
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 12 deletions.
2 changes: 2 additions & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@koa/router": "^10.1.1",
"@node-rs/crc32": "^1.5.1",
"basic-auth": "^2.0.1",
"buffer-crc32": "https://github.com/rejetto/buffer-crc32.git",
"cidr-tools": "^4.3.0",
"fast-glob": "^3.2.7",
Expand All @@ -30,6 +31,7 @@
},
"devDependencies": {
"@types/archiver": "^5.1.1",
"@types/basic-auth": "^1.1.3",
"@types/koa": "^2.13.4",
"@types/koa__router": "^8.0.11",
"@types/koa-compress": "^4.0.3",
Expand Down
27 changes: 18 additions & 9 deletions server/src/api.auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of HFS - Copyright 2021-2022, Massimo Melina <[email protected]> - License https://www.gnu.org/licenses/gpl-3.0.txt

import { getAccount, getCurrentUsername } from './perm'
import { Account, getAccount, getCurrentUsername } from './perm'
import { verifyPassword } from './crypt'
import { ApiError, ApiHandler } from './apiMiddleware'
import { SRPParameters, SRPRoutines, SRPServerSession, SRPServerSessionStep1 } from 'tssrp6a'
Expand All @@ -12,7 +12,6 @@ import { ctxAdminAccess } from './adminApis'
import { prepareState } from './middlewares'

const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters())
const srpSession = new SRPServerSession(srp6aNimbusRoutines)
const ongoingLogins:Record<string,SRPServerSessionStep1> = {} // store data that doesn't fit session object

// centralized log-in state
Expand Down Expand Up @@ -61,16 +60,26 @@ export const loginSrp1: ApiHandler = async ({ username }, ctx) => {
return new ApiError(500)
if (!account) // TODO simulate fake account to prevent knowing valid usernames
return new ApiError(UNAUTHORIZED)
try {
const { step1, ...rest } = await srpStep1(account)
const sid = Math.random()
ongoingLogins[sid] = step1
setTimeout(()=> delete ongoingLogins[sid], 60_000)
ctx.session.login = { username, sid }
return rest
}
catch (code: any) {
return new ApiError(code)
}
}

export async function srpStep1(account: Account) {
if (!account.srp)
return new ApiError(406) // unacceptable
throw 406 // unacceptable
const [salt, verifier] = account.srp.split('|')
const srpSession = new SRPServerSession(srp6aNimbusRoutines)
const step1 = await srpSession.step1(account.username, BigInt(salt), BigInt(verifier))
const sid = Math.random()
ongoingLogins[sid] = step1
setTimeout(()=> delete ongoingLogins[sid], 60_000)

ctx.session.login = { username, sid }
return { salt, pubKey: String(step1.B) } // cast to string cause bigint can't be jsonized
return { step1, salt, pubKey: String(step1.B) } // cast to string cause bigint can't be jsonized
}

export const loginSrp2: ApiHandler = async ({ pubKey, proof }, ctx) => {
Expand Down
24 changes: 23 additions & 1 deletion server/src/middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import { Readable } from 'stream'
import { applyBlock } from './block'
import { getAccount, getCurrentUsername } from './perm'
import { socket2connection, updateConnection, normalizeIp } from './connections'
import basicAuth from 'basic-auth'
import { SRPClientSession, SRPParameters, SRPRoutines } from 'tssrp6a'
import { srpStep1 } from './api.auth'

export const gzipper = compress({
threshold: 2048,
Expand Down Expand Up @@ -78,6 +81,7 @@ export const serveGuiAndSharedFiles: Koa.Middleware = async (ctx, next) => {
// this folder was requested without the trailing / and we may still log in
if (isFolder && !path.endsWith('/') && !ctx.state.account)
return ctx.redirect(path + '/')
ctx.set('WWW-Authenticate', 'Basic')
return serveFrontendFiles(ctx, next)
}
const { get } = ctx.query
Expand Down Expand Up @@ -119,9 +123,27 @@ export function getProxyDetected() {
}
export const prepareState: Koa.Middleware = async (ctx, next) => {
// calculate these once and for all
ctx.state.account = getAccount(getCurrentUsername(ctx))
ctx.state.account = await getHttpAccount(ctx) ?? getAccount(getCurrentUsername(ctx))
const conn = ctx.state.connection = socket2connection(ctx.socket)
if (conn)
updateConnection(conn, { ctx })
await next()
}

async function getHttpAccount(ctx: Koa.Context) {
const credentials = basicAuth(ctx.req)
const account = getAccount(credentials?.name||'')
if (account && await srpCheck(account.username, credentials!.pass))
return account
}

async function srpCheck(username: string, password: string) {
username = username.toLocaleLowerCase()
const account = getAccount(username)
if (!account?.srp) return false
const { step1, salt, pubKey } = await srpStep1(account)
const client = new SRPClientSession(new SRPRoutines(new SRPParameters()))
const clientRes1 = await client.step1(username, password)
const clientRes2 = await clientRes1.step2(BigInt(salt), BigInt(pubKey))
return await step1.step2(clientRes2.A, clientRes2.M1).then(() => true, () => false)
}
2 changes: 1 addition & 1 deletion server/src/perm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function getAccounts() {
}

export function getCurrentUsername(ctx: Koa.Context): string {
return ctx.session?.username || ''
return ctx.state.account?.username || ctx.session?.username || ''
}

// provides the username and all other usernames it inherits based on the 'belongs' attribute. Useful to check permissions
Expand Down
1 change: 0 additions & 1 deletion todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
- show public ip use, https://github.com/sindresorhus/public-ip
- configure router with upnp. If it fails, suggest a guide
- offer ddns registration/update
- support http authentication
- use dialogs instead of side-forms on mobile (admin/fs+accounts)
- admin/fs: sort items
- admin/fs: render virtual folders differently
Expand Down

0 comments on commit ef589f2

Please sign in to comment.