4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
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;
}
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 then
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.auto = 'current-password';
elseif creds.otp() then
ch.challenge = 'enter a valid one-time password for your account'
ch.label = 'OTP code'
ch.method = 'otp'
ch.auto = 'one-time-code';
elseif creds.challenge() then
ch.challenge = 'sign the challenge token: <code>...</code>'
ch.label = 'digest'
ch.method = 'challenge'
ch.auto = 'one-time-code';
else
co:complain(500,'login failure','unknown login method')
return
end
doc.body = ch:tostr()
else
-- pick a method
end
co:stdpage(doc)
doc.body:free()
end
return login_form
|
>
>
|
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
|
>
>
>
>
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
<
>
|
<
|
>
>
>
>
>
>
>
>
>
|
|
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
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
|