+56
-9
server/handle_account_signin.go
+56
-9
server/handle_account_signin.go
···
2
2
3
3
import (
4
4
"errors"
5
+
"fmt"
5
6
"strings"
7
+
"time"
6
8
7
9
"github.com/bluesky-social/indigo/atproto/syntax"
8
10
"github.com/gorilla/sessions"
···
15
17
)
16
18
17
19
type OauthSigninInput struct {
18
-
Username string `form:"username"`
19
-
Password string `form:"password"`
20
-
QueryParams string `form:"query_params"`
20
+
Username string `form:"username"`
21
+
Password string `form:"password"`
22
+
AuthFactorToken string `form:"token"`
23
+
QueryParams string `form:"query_params"`
21
24
}
22
25
23
26
func (s *Server) getSessionRepoOrErr(e echo.Context) (*models.RepoActor, *sessions.Session, error) {
···
44
47
func getFlashesFromSession(e echo.Context, sess *sessions.Session) map[string]any {
45
48
defer sess.Save(e.Request(), e.Response())
46
49
return map[string]any{
47
-
"errors": sess.Flashes("error"),
48
-
"successes": sess.Flashes("success"),
50
+
"errors": sess.Flashes("error"),
51
+
"successes": sess.Flashes("success"),
52
+
"tokenrequired": sess.Flashes("tokenrequired"),
49
53
}
50
54
}
51
55
···
83
87
idtype = "email"
84
88
}
85
89
90
+
queryParams := ""
91
+
if req.QueryParams != "" {
92
+
queryParams = fmt.Sprintf("?%s", req.QueryParams)
93
+
}
94
+
86
95
// TODO: we should make this a helper since we do it for the base create_session as well
87
96
var repo models.RepoActor
88
97
var err error
···
101
110
sess.AddFlash("Something went wrong!", "error")
102
111
}
103
112
sess.Save(e.Request(), e.Response())
104
-
return e.Redirect(303, "/account/signin")
113
+
return e.Redirect(303, "/account/signin"+queryParams)
105
114
}
106
115
107
116
if err := bcrypt.CompareHashAndPassword([]byte(repo.Password), []byte(req.Password)); err != nil {
···
111
120
sess.AddFlash("Something went wrong!", "error")
112
121
}
113
122
sess.Save(e.Request(), e.Response())
114
-
return e.Redirect(303, "/account/signin")
123
+
return e.Redirect(303, "/account/signin"+queryParams)
124
+
}
125
+
126
+
// if repo requires auth factor token and one hasn't been provided, return error prompting for one
127
+
if repo.EmailAuthFactor && req.AuthFactorToken == "" {
128
+
err = s.createAndSendAuthCode(ctx, repo)
129
+
if err != nil {
130
+
sess.AddFlash("Something went wrong!", "error")
131
+
sess.Save(e.Request(), e.Response())
132
+
return e.Redirect(303, "/account/signin"+queryParams)
133
+
}
134
+
135
+
sess.AddFlash("requires 2FA token", "tokenrequired")
136
+
sess.Save(e.Request(), e.Response())
137
+
return e.Redirect(303, "/account/signin"+queryParams)
138
+
}
139
+
140
+
// if auth factor is required, now check that the one provided is valid
141
+
if repo.EmailAuthFactor {
142
+
if repo.AuthCode == nil || repo.AuthCodeExpiresAt == nil {
143
+
err = s.createAndSendAuthCode(ctx, repo)
144
+
if err != nil {
145
+
sess.AddFlash("Something went wrong!", "error")
146
+
sess.Save(e.Request(), e.Response())
147
+
return e.Redirect(303, "/account/signin"+queryParams)
148
+
}
149
+
150
+
sess.AddFlash("requires 2FA token", "tokenrequired")
151
+
sess.Save(e.Request(), e.Response())
152
+
return e.Redirect(303, "/account/signin"+queryParams)
153
+
}
154
+
155
+
if *repo.AuthCode != req.AuthFactorToken {
156
+
return helpers.InvalidTokenError(e)
157
+
}
158
+
159
+
if time.Now().UTC().After(*repo.AuthCodeExpiresAt) {
160
+
return helpers.ExpiredTokenError(e)
161
+
}
115
162
}
116
163
117
164
sess.Options = &sessions.Options{
···
127
174
return err
128
175
}
129
176
130
-
if req.QueryParams != "" {
131
-
return e.Redirect(303, "/oauth/authorize?"+req.QueryParams)
177
+
if queryParams != "" {
178
+
return e.Redirect(303, "/oauth/authorize"+queryParams)
132
179
} else {
133
180
return e.Redirect(303, "/account")
134
181
}
+8
-8
server/handle_server_create_session.go
+8
-8
server/handle_server_create_session.go
···
87
87
return helpers.ServerError(e, nil)
88
88
}
89
89
90
+
if err := bcrypt.CompareHashAndPassword([]byte(repo.Password), []byte(req.Password)); err != nil {
91
+
if err != bcrypt.ErrMismatchedHashAndPassword {
92
+
logger.Error("erorr comparing hash and password", "error", err)
93
+
}
94
+
return helpers.InputError(e, to.StringPtr("InvalidRequest"))
95
+
}
96
+
90
97
// if repo requires auth factor token and one hasn't been provided, return error prompting for one
91
98
if repo.EmailAuthFactor && (req.AuthFactorToken == nil || *req.AuthFactorToken == "") {
92
99
err = s.createAndSendAuthCode(ctx, repo)
···
103
110
if repo.AuthCode == nil || repo.AuthCodeExpiresAt == nil {
104
111
err = s.createAndSendAuthCode(ctx, repo)
105
112
if err != nil {
106
-
s.logger.Error("sending auth code", "error", err)
113
+
logger.Error("sending auth code", "error", err)
107
114
return helpers.ServerError(e, nil)
108
115
}
109
116
···
117
124
if time.Now().UTC().After(*repo.AuthCodeExpiresAt) {
118
125
return helpers.ExpiredTokenError(e)
119
126
}
120
-
}
121
-
122
-
if err := bcrypt.CompareHashAndPassword([]byte(repo.Password), []byte(req.Password)); err != nil {
123
-
if err != bcrypt.ErrMismatchedHashAndPassword {
124
-
logger.Error("erorr comparing hash and password", "error", err)
125
-
}
126
-
return helpers.InputError(e, to.StringPtr("InvalidRequest"))
127
127
}
128
128
129
129
sess, err := s.createSession(ctx, &repo.Repo)
+4
server/templates/signin.html
+4
server/templates/signin.html
···
26
26
type="password"
27
27
placeholder="Password"
28
28
/>
29
+
{{ if .flashes.tokenrequired }}
30
+
<br />
31
+
<input name="token" id="token" placeholder="Enter your 2FA token" />
32
+
{{ end }}
29
33
<input name="query_params" type="hidden" value="{{ .QueryParams }}" />
30
34
<button class="primary" type="submit" value="Login">Login</button>
31
35
</form>