server.go 19.6 KB
Newer Older
1
package keyshareserver
2
3

import (
4
	"bytes"
5
	"context"
6
7
8
9
10
11
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"sync"
12
	"time"
13

14
15
	"github.com/go-errors/errors"
	"github.com/hashicorp/go-multierror"
16
	"github.com/jasonlvhit/gocron"
17
18
19
20
21
	"github.com/privacybydesign/gabi"
	"github.com/privacybydesign/gabi/big"
	irma "github.com/privacybydesign/irmago"
	"github.com/sirupsen/logrus"

22
	"github.com/privacybydesign/irmago/internal/common"
23
	"github.com/privacybydesign/irmago/internal/keysharecore"
24
25
26
27
28
29
30
	"github.com/privacybydesign/irmago/server"
	"github.com/privacybydesign/irmago/server/irmaserver"

	"github.com/go-chi/chi"
)

type SessionData struct {
31
	LastKeyID    irma.PublicKeyIdentifier // last used key, used in signing the issuance message
32
	LastCommitID uint64
33
	expiry       time.Time
34
35
36
}

type Server struct {
37
	// configuration
38
39
	conf *Configuration

40
	// external components
41
	core          *keysharecore.Core
42
43
	sessionserver *irmaserver.Server
	db            KeyshareDB
44
45

	// Scheduler used to clean sessions
46
47
	scheduler     *gocron.Scheduler
	stopScheduler chan<- bool
48

49
	// Session data, keeping track of current keyshare protocol session state for each user
50
51
52
53
54
55
56
	sessions    map[string]*SessionData
	sessionLock sync.Mutex
}

func New(conf *Configuration) (*Server, error) {
	var err error
	s := &Server{
57
58
59
		conf:      conf,
		sessions:  map[string]*SessionData{},
		scheduler: gocron.NewScheduler(),
60
61
	}

62
63
	// Setup IRMA session server
	s.sessionserver, err = irmaserver.New(conf.Configuration)
64
65
66
67
	if err != nil {
		return nil, err
	}

68
69
	// Process configuration and create keyshare core
	s.core, err = processConfiguration(conf)
70
71
72
73
	if err != nil {
		return nil, err
	}

74
75
	// Load neccessary idemix keys into core, and ensure that future updates
	// to them are processed
76
77
78
79
80
81
82
83
84
	if err = s.LoadIdemixKeys(conf.IrmaConfiguration); err != nil {
		return nil, err
	}
	conf.IrmaConfiguration.UpdateListeners = append(conf.IrmaConfiguration.UpdateListeners, func(conf *irma.Configuration) {
		if err := s.LoadIdemixKeys(conf); err != nil {
			// run periodically; can only log the error here
			_ = server.LogError(err)
		}
	})
85

86
87
	// Setup DB
	s.db = conf.DB
88

89
90
91
92
	// Setup session cache clearing
	s.scheduler.Every(10).Seconds().Do(s.clearSessions)
	s.stopScheduler = s.scheduler.Start()

93
94
95
	return s, nil
}

96
97
98
99
100
func (s *Server) Stop() {
	s.stopScheduler <- true
	s.sessionserver.Stop()
}

101
// clean up any expired sessions
102
103
104
105
106
107
108
109
110
111
112
func (s *Server) clearSessions() {
	now := time.Now()
	s.sessionLock.Lock()
	defer s.sessionLock.Unlock()
	for k, v := range s.sessions {
		if now.After(v.expiry) {
			delete(s.sessions, k)
		}
	}
}

113
114
func (s *Server) Handler() http.Handler {
	router := chi.NewRouter()
115
116
117

	if s.conf.Verbose >= 2 {
		opts := server.LogOptions{Response: true, Headers: true, From: false, EncodeBinary: true}
118
		router.Use(server.LogMiddleware("keyshareserver", opts))
119
120
	}

121
	// Registration
122
	router.Post("/client/register", s.handleRegister)
123

124
	// Pin logic
125
126
	router.Post("/users/verify/pin", s.handleVerifyPin)
	router.Post("/users/change/pin", s.handleChangePin)
127
128

	// Keyshare sessions
129
130
131
132
133
134
135
	router.Group(func(router chi.Router) {
		router.Use(s.userMiddleware)
		router.Use(s.authorizationMiddleware)
		router.Post("/users/isAuthorized", s.handleValidate)
		router.Post("/prove/getCommitments", s.handleCommitments)
		router.Post("/prove/getResponse", s.handleResponse)
	})
136

137
	// IRMA server for issuing myirma credential during registration
138
139
140
141
	router.Mount("/irma/", s.sessionserver.HandlerFunc())
	return router
}

142
// On configuration changes, inform the keyshare core of any
143
// new IRMA issuer public keys.
144
145
func (s *Server) LoadIdemixKeys(conf *irma.Configuration) error {
	errs := multierror.Error{}
146
	for _, issuer := range conf.Issuers {
147
		keyIDs, err := conf.PublicKeyIndices(issuer.Identifier())
148
		if err != nil {
149
			errs.Errors = append(errs.Errors, errors.Errorf("issuer %v: could not find key IDs: %v", issuer, err))
150
151
			continue
		}
152
		for _, id := range keyIDs {
153
154
			key, err := conf.PublicKey(issuer.Identifier(), id)
			if err != nil {
155
				errs.Errors = append(errs.Errors, server.LogError(errors.Errorf("key %v-%v: could not fetch public key: %v", issuer, id, err)))
156
157
				continue
			}
158
			s.core.DangerousAddTrustedPublicKey(irma.PublicKeyIdentifier{Issuer: issuer.Identifier(), Counter: id}, key)
159
160
		}
	}
161
	return errs.ErrorOrNil()
162
163
}

164
// /prove/getCommitments
165
func (s *Server) handleCommitments(w http.ResponseWriter, r *http.Request) {
166
	// Fetch from context
167
	user := r.Context().Value("user").(*KeyshareUser)
168
169
	authorization := r.Context().Value("authorization").(string)

170
	// Read keys
171
	var keys []irma.PublicKeyIdentifier
172
	if err := s.parseBody(w, r, &keys); err != nil {
173
174
175
176
177
178
179
180
181
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
	}
	if len(keys) == 0 {
		s.conf.Logger.Info("Malformed request: no keys over which to commit specified")
		server.WriteError(w, server.ErrorInvalidRequest, "No key specified")
		return
	}

182
	commitments, err := s.generateCommitments(user, authorization, keys)
183
	if err != nil && (err == keysharecore.ErrInvalidChallenge || err == keysharecore.ErrInvalidJWT) {
184
185
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
186
	}
187
	if err != nil {
188
189
		// already logged
		server.WriteError(w, server.ErrorInternal, err.Error())
190
191
192
		return
	}

193
194
195
	server.WriteJson(w, commitments)
}

196
func (s *Server) generateCommitments(user *KeyshareUser, authorization string, keys []irma.PublicKeyIdentifier) (*irma.ProofPCommitmentMap, error) {
197
	// Generate commitments
198
	commitments, commitID, err := s.core.GenerateCommitments(user.Coredata, authorization, keys)
199
200
	if err != nil {
		s.conf.Logger.WithField("error", err).Warn("Could not generate commitments for request")
201
		return nil, err
202
203
204
	}

	// Prepare output message format
205
	mappedCommitments := map[irma.PublicKeyIdentifier]*gabi.ProofPCommitment{}
206
	for i, keyID := range keys {
207
		mappedCommitments[keyID] = commitments[i]
208
209
210
	}

	// Store needed data for later requests.
211
	username := user.Username
212
	s.sessionLock.Lock()
213
214
215
216
	session, ok := s.sessions[username]
	if !ok {
		session = &SessionData{}
		s.sessions[username] = session
217
	}
218
219
220
221
	// Of all keys involved in the current session, store the ID of the first one to be used when
	// the user comes back later to retrieve her response. gabi.ProofP.P will depend on this public
	// key, which is used only during issuance. Thus, this assumes that during issuance, the user
	// puts the key ID of the credential(s) being issued at index 0.
222
223
224
	session.LastKeyID = keys[0]
	session.LastCommitID = commitID
	session.expiry = time.Now().Add(10 * time.Second)
225
226
227
	s.sessionLock.Unlock()

	// And send response
228
	return &irma.ProofPCommitmentMap{Commitments: mappedCommitments}, nil
229
230
}

231
// /prove/getResponse
232
func (s *Server) handleResponse(w http.ResponseWriter, r *http.Request) {
233
	// Fetch from context
234
	user := r.Context().Value("user").(*KeyshareUser)
235
236
	authorization := r.Context().Value("authorization").(string)

237
238
	// Read challenge
	challenge := new(big.Int)
239
	if err := s.parseBody(w, r, challenge); err != nil {
240
241
242
243
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
	}

244
245
246
247
248
	// verify access (avoids leaking whether there is a session ongoing to unauthorized callers)
	if !r.Context().Value("hasValidAuthorization").(bool) {
		s.conf.Logger.Warn("Could not generate keyshare response due to invalid authorization")
		server.WriteError(w, server.ErrorInvalidRequest, "Invalid authorization")
		return
249
	}
250

251
252
	// Get data from session
	s.sessionLock.Lock()
253
	sessionData, ok := s.sessions[user.Username]
254
255
256
257
	s.sessionLock.Unlock()
	if !ok {
		s.conf.Logger.Warn("Request for response without previous call to get commitments")
		server.WriteError(w, server.ErrorInvalidRequest, "Missing previous call to getCommitments")
258
259
260
		return
	}

261
262
	// And do the actual responding
	proofResponse, err := s.doGenerateResponses(user, authorization, challenge, sessionData.LastCommitID, sessionData.LastKeyID)
263
	if err != nil && (err == keysharecore.ErrInvalidChallenge || err == keysharecore.ErrInvalidJWT) {
264
265
266
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
	}
267
268
269
270
271
272
273
274
	if err != nil {
		// already logged
		server.WriteError(w, server.ErrorInternal, err.Error())
		return
	}

	server.WriteString(w, proofResponse)
}
275

276
func (s *Server) doGenerateResponses(user *KeyshareUser, authorization string, challenge *big.Int, commitID uint64, keyID irma.PublicKeyIdentifier) (string, error) {
David Venhoek's avatar
David Venhoek committed
277
	// Indicate activity on user account
278
	err := s.db.SetSeen(user)
David Venhoek's avatar
David Venhoek committed
279
280
281
282
283
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not mark user as seen recently")
		// Do not send to user
	}

284
285
286
287
	// Make log entry
	err = s.db.AddLog(user, IrmaSession, nil)
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not add log entry for user")
288
		return "", err
289
290
	}

291
	proofResponse, err := s.core.GenerateResponse(user.Coredata, authorization, commitID, challenge, keyID)
292
293
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not generate response for request")
294
		return "", err
295
296
	}

297
	return proofResponse, nil
298
299
}

300
// /users/isAuthorized
301
func (s *Server) handleValidate(w http.ResponseWriter, r *http.Request) {
302
	if r.Context().Value("hasValidAuthorization").(bool) {
303
		server.WriteJson(w, &irma.KeyshareAuthorization{Status: "authorized", Candidates: []string{"pin"}})
304
	} else {
305
		server.WriteJson(w, &irma.KeyshareAuthorization{Status: "expired", Candidates: []string{"pin"}})
306
307
308
	}
}

309
// /users/verify/pin
310
311
func (s *Server) handleVerifyPin(w http.ResponseWriter, r *http.Request) {
	// Extract request
312
	var msg irma.KeysharePinMessage
313
	if err := s.parseBody(w, r, &msg); err != nil {
314
315
316
317
318
319
320
321
322
323
324
325
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
	}

	// Fetch user
	user, err := s.db.User(msg.Username)
	if err != nil {
		s.conf.Logger.WithFields(logrus.Fields{"username": msg.Username, "error": err}).Warn("Could not find user in db")
		server.WriteError(w, server.ErrorUserNotRegistered, "")
		return
	}

326
327
	// and verify pin
	result, err := s.doVerifyPin(user, msg.Username, msg.Pin)
328
	if err != nil {
329
		// already logged
330
331
332
		server.WriteError(w, server.ErrorInternal, err.Error())
		return
	}
333
334
335
336

	server.WriteJson(w, result)
}

337
func (s *Server) doVerifyPin(user *KeyshareUser, username, pin string) (irma.KeysharePinStatus, error) {
338
339
	// Check whether pin check is currently allowed
	ok, tries, wait, err := s.reservePinCheck(user, pin)
340
	if err != nil {
341
		return irma.KeysharePinStatus{}, err
342
	}
343
	if !ok {
344
		return irma.KeysharePinStatus{Status: "error", Message: fmt.Sprintf("%v", wait)}, nil
345
	}
346

347
	// At this point, we are allowed to do an actual check (we have successfully reserved a spot for it), so do it.
348
	jwtt, err := s.core.ValidatePin(user.Coredata, pin, username)
349
350
351
	if err != nil && err != keysharecore.ErrInvalidPin {
		// Errors other than invalid pin are real errors
		s.conf.Logger.WithField("error", err).Error("Could not validate pin")
352
		return irma.KeysharePinStatus{}, err
353
	}
354

355
	if err == keysharecore.ErrInvalidPin {
356
		// Handle invalid pin
357
358
359
		err = s.db.AddLog(user, PinCheckFailed, tries)
		if err != nil {
			s.conf.Logger.WithField("error", err).Error("Could not add log entry for user")
360
			return irma.KeysharePinStatus{}, err
361
		}
362
		if tries == 0 {
363
364
365
			err = s.db.AddLog(user, PinCheckBlocked, wait)
			if err != nil {
				s.conf.Logger.WithField("error", err).Error("Could not add log entry for user")
366
				return irma.KeysharePinStatus{}, err
367
			}
368
			return irma.KeysharePinStatus{Status: "error", Message: fmt.Sprintf("%v", wait)}, nil
369
		} else {
370
			return irma.KeysharePinStatus{Status: "failure", Message: fmt.Sprintf("%v", tries)}, nil
371
		}
372
	}
373

374
375
376
377
378
379
380
381
382
383
384
385
386
387
	// Handle success
	err = s.db.ClearPincheck(user)
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not reset users pin check logic")
		// Do not send to user
	}
	err = s.db.SetSeen(user)
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not indicate user activity")
		// Do not send to user
	}
	err = s.db.AddLog(user, PinCheckSuccess, nil)
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not add log entry for user")
388
		return irma.KeysharePinStatus{}, err
389
	}
390

391
	return irma.KeysharePinStatus{Status: "success", Message: jwtt}, err
392
393
}

394
// /users/change/pin
395
396
func (s *Server) handleChangePin(w http.ResponseWriter, r *http.Request) {
	// Extract request
397
	var msg irma.KeyshareChangePin
398
399
	if err := s.parseBody(w, r, &msg); err != nil {
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
400
401
402
403
404
405
406
407
408
409
410
		return
	}

	// Fetch user
	user, err := s.db.User(msg.Username)
	if err != nil {
		s.conf.Logger.WithFields(logrus.Fields{"username": msg.Username, "error": err}).Warn("Could not find user in db")
		server.WriteError(w, server.ErrorUserNotRegistered, "")
		return
	}

411
	result, err := s.doUpdatePin(user, msg.OldPin, msg.NewPin)
412
	if err != nil {
413
		// already logged
414
		server.WriteError(w, server.ErrorInternal, err.Error())
415
416
		return
	}
417
418
419
	server.WriteJson(w, result)
}

420
func (s *Server) doUpdatePin(user *KeyshareUser, oldPin, newPin string) (irma.KeysharePinStatus, error) {
421
	// Check whether pin check is currently allowed
422
	ok, tries, wait, err := s.reservePinCheck(user, oldPin)
423
	if err != nil {
424
		return irma.KeysharePinStatus{}, err
425
	}
426
	if !ok {
427
		return irma.KeysharePinStatus{Status: "error", Message: fmt.Sprintf("%v", wait)}, nil
428
	}
429
430

	// Try to do the update
431
	user.Coredata, err = s.core.ChangePin(user.Coredata, oldPin, newPin)
432
	if err == keysharecore.ErrInvalidPin {
433
		if tries == 0 {
434
			return irma.KeysharePinStatus{Status: "error", Message: fmt.Sprintf("%v", wait)}, nil
435
		} else {
436
			return irma.KeysharePinStatus{Status: "failure", Message: fmt.Sprintf("%v", tries)}, nil
437
438
439
		}
	} else if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not change pin")
440
		return irma.KeysharePinStatus{}, err
441
	}
442
443

	// Mark pincheck as success, resetting users wait and count
444
445
446
447
448
	err = s.db.ClearPincheck(user)
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not reset users pin check logic")
		// Do not send to user
	}
449
450
451
452
453

	// Write user back
	err = s.db.UpdateUser(user)
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not write updated user to database")
454
		return irma.KeysharePinStatus{}, err
455
456
	}

457
	return irma.KeysharePinStatus{Status: "success"}, nil
458
459
}

460
// /client/register
461
462
func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
	// Extract request
463
	var msg irma.KeyshareEnrollment
464
465
	if err := s.parseBody(w, r, &msg); err != nil {
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
466
467
468
		return
	}

469
	sessionptr, err := s.doRegistration(msg)
470
	if err != nil && err == keysharecore.ErrPinTooLong {
471
472
473
474
475
476
477
478
479
480
481
482
		// Too long pin is not an internal error
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
	}
	if err != nil {
		// Already logged
		server.WriteError(w, server.ErrorInternal, err.Error())
		return
	}
	server.WriteJson(w, sessionptr)
}

483
func (s *Server) doRegistration(msg irma.KeyshareEnrollment) (*irma.Qr, error) {
484
	// Generate keyshare server account
485
486
487
	username := common.NewSessionToken() // TODO use newRandomString() for this when shoulder-surf is merged
	username = username[:12]

488
489
490
	coredata, err := s.core.GenerateKeyshareSecret(msg.Pin)
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not register user")
491
		return nil, err
492
	}
493
494
	user := &KeyshareUser{Username: username, Language: msg.Language, Coredata: coredata}
	err = s.db.NewUser(user)
495
496
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not store new user in database")
497
		return nil, err
498
499
	}

500
	// Send email if user specified email address
501
	if msg.Email != nil && *msg.Email != "" && s.conf.EmailServer != "" {
502
		err = s.sendRegistrationEmail(user, msg.Language, *msg.Email)
503
		if err != nil {
504
505
			// already logged in sendRegistrationEmail
			return nil, err
506
		}
507
508
	}

509
510
511
512
513
514
515
516
517
518
519
	// Setup and return issuance session for keyshare credential.
	request := irma.NewIssuanceRequest([]*irma.CredentialRequest{
		{
			CredentialTypeID: irma.NewCredentialTypeIdentifier(s.conf.KeyshareCredential),
			Attributes: map[string]string{
				s.conf.KeyshareAttribute: username,
			},
		}})
	sessionptr, _, err := s.sessionserver.StartSession(request, nil)
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not start keyshare credential issuance sessions")
520
		return nil, err
521
	}
522
523
524
	return sessionptr, nil
}

525
func (s *Server) sendRegistrationEmail(user *KeyshareUser, language, email string) error {
526
	// Fetch template and configuration data for users language, falling back if needed
527
	template, ok := s.conf.registrationEmailTemplates[language]
528
	if !ok {
529
		template = s.conf.registrationEmailTemplates[s.conf.DefaultLanguage]
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
	}
	verificationBaseURL, ok := s.conf.VerificationURL[language]
	if !ok {
		verificationBaseURL = s.conf.VerificationURL[s.conf.DefaultLanguage]
	}
	subject, ok := s.conf.RegistrationEmailSubject[language]
	if !ok {
		subject = s.conf.RegistrationEmailSubject[s.conf.DefaultLanguage]
	}

	// Generate token
	token := common.NewSessionToken()

	// Add it to the database
	err := s.db.AddEmailVerification(user, email, token)
	if err != nil {
546
		s.conf.Logger.WithField("error", err).Error("Could not generate email verification mail record")
547
548
549
550
551
552
553
		return err
	}

	// Build message
	var msg bytes.Buffer
	err = template.Execute(&msg, map[string]string{"VerificationURL": verificationBaseURL + token})
	if err != nil {
554
		s.conf.Logger.WithField("error", err).Error("Could not generate email verification mail")
555
556
557
558
559
560
561
562
563
564
565
566
567
		return err
	}

	// And send it
	err = server.SendHTMLMail(
		s.conf.EmailServer,
		s.conf.EmailAuth,
		s.conf.EmailFrom,
		email,
		subject,
		msg.Bytes())

	if err != nil {
568
		s.conf.Logger.WithField("error", err).Error("Could not send email verification mail")
569
570
571
572
		return err
	}

	return nil
573
574
}

575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
func (s *Server) userMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Extract username from request
		username := r.Header.Get("X-IRMA-Keyshare-Username")

		// and fetch its information
		user, err := s.db.User(username)
		if err != nil {
			s.conf.Logger.WithFields(logrus.Fields{"username": username, "error": err}).Warn("Could not find user in db")
			server.WriteError(w, server.ErrorUserNotRegistered, err.Error())
			return
		}

		next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "user", user)))
	})
}

func (s *Server) authorizationMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Extract authorization from request
		authorization := r.Header.Get("Authorization")
		if strings.HasPrefix(authorization, "Bearer ") {
			authorization = authorization[7:]
		}

		// verify access
		ctx := r.Context()
602
		err := s.core.ValidateJWT(ctx.Value("user").(*KeyshareUser).Coredata, authorization)
603
		hasValidAuthorization := err == nil
604
605
606
607
608
609
610
611
612

		// Construct new context with both authorization and its validity
		nextContext := context.WithValue(
			context.WithValue(ctx, "authorization", authorization),
			"hasValidAuthorization", hasValidAuthorization)

		next.ServeHTTP(w, r.WithContext(nextContext))
	})
}
613
614
615
616
617
618
619
620
621
622
623
624
625
626

func (s *Server) parseBody(w http.ResponseWriter, r *http.Request, input interface{}) error {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		s.conf.Logger.WithField("error", err).Info("Malformed request: could not read request body")
		return err
	}
	err = json.Unmarshal(body, input)
	if err != nil {
		s.conf.Logger.WithField("error", err).Info("Malformed request: could not parse request body")
		return err
	}
	return nil
}
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643

func (s *Server) reservePinCheck(user *KeyshareUser, pin string) (bool, int, int64, error) {
	ok, tries, wait, err := s.db.ReservePincheck(user)
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not reserve pin check slot")
		return false, 0, 0, err
	}
	if !ok {
		err = s.db.AddLog(user, PinCheckRefused, nil)
		if err != nil {
			s.conf.Logger.WithField("error", err).Error("Could not add log entry for user")
			return false, 0, 0, err
		}
		return false, tries, wait, nil
	}
	return true, tries, wait, nil
}