Differences From
Artifact [434636bebc]:
4 4 login_form(co: &lib.srv.convo, user: &lib.store.actor, creds: &lib.store.credset, msg: pstr)
5 5 var doc = [lib.srv.convo.page] {
6 6 title = 'instance logon';
7 7 class = 'login';
8 8 cache = false;
9 9 }
10 10
11 + var how = co:ppostv('how')
12 +
11 13 if user == nil then
12 14 var form = data.view.login_username {
13 15 loginmsg = msg;
14 16 }
15 17 if form.loginmsg.ptr == nil then
16 18 form.loginmsg = 'identify yourself for access to this instance.'
17 19 end
18 20 doc.body = form:tostr()
19 21 elseif creds:sz() == 0 then
20 22 co:complain(403,'access denied','your host is not eligible to authenticate as this user')
21 23 return
22 - elseif creds:sz() == 1 then
24 + elseif creds:sz() == 1 or how:ref() then
25 + var newcreds: lib.store.credset
26 + if how:ref() then
27 + if how:cmp('pw') then newcreds = creds and [lib.store.credset.bits.pw]
28 + elseif how:cmp('chlg') then newcreds = creds and [lib.store.credset.bits.challenge]
29 + elseif how:cmp('otp') then newcreds = creds and [lib.store.credset.bits.otp]
30 + elseif how:cmp('trust') then newcreds = creds and [lib.store.credset.bits.trust]
31 + else co:complain(400, 'bad request', 'the requested authentication method is not available') return end
32 + creds = &newcreds
33 + end
34 +
23 35 if creds.trust() then
24 36 -- TODO log in immediately
25 37 return
26 38 end
27 39
28 40 var ch = data.view.login_challenge {
29 41 handle = user.handle;
30 42 name = lib.coalesce(user.nym, user.handle);
31 43 }
32 44 if creds.pw() then
33 45 ch.challenge = 'enter the password associated with your account'
34 46 ch.label = 'password'
35 47 ch.method = 'pw'
36 - ch.auto = 'current-password';
48 + ch.inputfield = '<input type="password" autocomplete="current-password" name="response" id="response" autofocus required>';
37 49 elseif creds.otp() then
38 50 ch.challenge = 'enter a valid one-time password for your account'
39 51 ch.label = 'OTP code'
40 52 ch.method = 'otp'
41 - ch.auto = 'one-time-code';
53 + ch.inputfield = '<input type="text" autocomplete="one-time-code" name="response" id="response" autofocus required>';
42 54 elseif creds.challenge() then
43 - ch.challenge = 'sign the challenge token: <code>...</code>'
55 + var tok = co:stra(128)
56 + var chlg = co:stra(128)
57 + var input = co:stra(256)
58 + var time = lib.osclock.time(nil)
59 +
60 + lib.crypt.cryptogram(&tok,6)
61 + chlg:lpush 'sign the challenge token <code style="display:block;user-select: all">'
62 + :push(tok.buf,tok.sz)
63 + :lpush '</code>'
64 +
65 + ch.challenge = chlg:finalize()
44 66 ch.label = 'digest'
45 67 ch.method = 'challenge'
46 - ch.auto = 'one-time-code';
68 + input:lpush '<textarea autocomplete="one-time-code" name="response" id="response" autofocus required></textarea><input type="hidden" name="time" value="'
69 + :shpush(time)
70 + :lpush '"><input type="hidden" name="token" value="'
71 + :push(tok.buf,tok.sz)
72 + tok:shpush(time)
73 + var hmac = lib.crypt.hmacp(&co.srv.pool,
74 + lib.crypt.alg.sha256,
75 + co.srv.cfg.secret:blob(),
76 + tok:finalize())
77 + input:lpush '"><input type="hidden" name="vfy" value="'
78 + :shpush(lib.math.truncate64(hmac.ptr, hmac.ct)) -- FIXME this is probably not very secure...
79 + :lpush '">'
80 +
81 + ch.inputfield = input:finalize()
47 82 else
48 - co:complain(500,'login failure','unknown login method')
83 + co:complain(400,'login failure','no usable login methods are available')
49 84 return
50 85 end
51 86
52 - doc.body = ch:tostr()
53 - else
54 - -- pick a method
87 + doc.body = ch:poolstr(&co.srv.pool)
88 + else -- pick a method
89 + var a = co:stra(400)
90 + var username = lib.html.sanitize(&co.srv.pool, pstr{user.handle,0}, true)
91 + 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'">'
92 + if creds.trust() then a:lpush '<button name="how" value="trust">trust</button>' end
93 + if creds.pw() then a:lpush '<button name="how" value="pw">password</button>' end
94 + if creds.otp() then a:lpush '<button name="how" value="otp">TOTP code</button>' end
95 + if creds.challenge() then a:lpush '<button name="how" value="chlg">challenge</button>' end
96 + a:lpush '</menu></form>'
97 + doc.body = a:finalize()
55 98 end
56 99
57 100 co:stdpage(doc)
58 - doc.body:free()
101 + --doc.body:free()
59 102 end
60 103
61 104 return login_form