-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local terra
login_form(co: &lib.srv.convo, user: &lib.store.actor, creds: &lib.store.credset, msg: pstr)
var doc = [lib.srv.convo.page] {
title = 'instance logon';
class = 'login';
cache = false;
}
var how = co:ppostv('how')
if user == nil then
var form = data.view.login_username {
loginmsg = msg;
}
if form.loginmsg.ptr == nil then
form.loginmsg = 'identify yourself for access to this instance.'
end
doc.body = form:tostr()
elseif creds:sz() == 0 then
co:complain(403,'access denied','your host is not eligible to authenticate as this user')
return
elseif creds:sz() == 1 or how:ref() then
var newcreds: lib.store.credset
if how:ref() then
if how:cmp('pw') then newcreds = creds and [lib.store.credset.bits.pw]
elseif how:cmp('chlg') then newcreds = creds and [lib.store.credset.bits.challenge]
elseif how:cmp('otp') then newcreds = creds and [lib.store.credset.bits.otp]
elseif how:cmp('trust') then newcreds = creds and [lib.store.credset.bits.trust]
else co:complain(400, 'bad request', 'the requested authentication method is not available') return end
creds = &newcreds
end
if creds.trust() then
-- TODO log in immediately
return
end
var ch = data.view.login_challenge {
handle = user.handle;
name = lib.coalesce(user.nym, user.handle);
}
if creds.pw() then
ch.challenge = 'enter the password associated with your account'
ch.label = 'password'
ch.method = 'pw'
ch.inputfield = '<input type="password" autocomplete="current-password" name="response" id="response" autofocus required>';
elseif creds.otp() then
ch.challenge = 'enter a valid one-time password for your account'
ch.label = 'OTP code'
ch.method = 'otp'
ch.inputfield = '<input type="text" autocomplete="one-time-code" name="response" id="response" autofocus required>';
elseif creds.challenge() then
var tok = co:stra(128)
var chlg = co:stra(128)
var input = co:stra(256)
var time = lib.osclock.time(nil)
lib.crypt.cryptogram(&tok,6)
chlg:lpush 'sign the challenge token <code style="display:block;user-select: all">'
:push(tok.buf,tok.sz)
:lpush '</code>'
ch.challenge = chlg:finalize()
ch.label = 'digest'
ch.method = 'challenge'
input:lpush '<textarea autocomplete="one-time-code" name="response" id="response" autofocus required></textarea><input type="hidden" name="time" value="'
:shpush(time)
:lpush '"><input type="hidden" name="token" value="'
:push(tok.buf,tok.sz)
tok:shpush(time)
var hmac = lib.crypt.hmacp(&co.srv.pool,
lib.crypt.alg.sha256,
co.srv.cfg.secret:blob(),
tok:finalize())
input:lpush '"><input type="hidden" name="vfy" value="'
:shpush(lib.math.truncate64(hmac.ptr, hmac.ct)) -- FIXME this is probably not very secure...
:lpush '">'
ch.inputfield = input:finalize()
else
co:complain(400,'login failure','no usable login methods are available')
return
end
doc.body = ch:poolstr(&co.srv.pool)
else -- pick a method
var a = co:stra(400)
var username = lib.html.sanitize(&co.srv.pool, pstr{user.handle,0}, true)
a:lpush '<form action="/login" method="post" class="auth-select"><p>multiple authentication mechanisms are available. select one to continue.</p><menu><input type="hidden" name="user" value="':ppush(username):lpush'">'
if creds.trust() then a:lpush '<button name="how" value="trust">trust</button>' end
if creds.pw() then a:lpush '<button name="how" value="pw">password</button>' end
if creds.otp() then a:lpush '<button name="how" value="otp">TOTP code</button>' end
if creds.challenge() then a:lpush '<button name="how" value="chlg">challenge</button>' end
a:lpush '</menu></form>'
doc.body = a:finalize()
end
co:stdpage(doc)
--doc.body:free()
end
return login_form