in lib/cli/cli.go [415:483]
func (h *AcceptHandler) Handle(w http.ResponseWriter, r *http.Request) {
name := httputils.QueryParam(r, "state")
if name == "" {
// Error state, provide page content to display error (hash messages on page can override).
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.InvalidArgument, "missing state parameter"))
return
}
if name == "" {
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.InvalidArgument, "login failed: missing state query parameter"))
return
}
item := &cpb.CliState{}
if err := h.store.ReadTx(storage.CliAuthDatatype, getRealm(r), storage.DefaultUser, name, storage.LatestRev, item, nil); err != nil {
if storage.ErrNotFound(err) {
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.NotFound, "login %q not found", name))
return
}
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.Unavailable, "load login %q failed: storage is unavailable", name))
return
}
// Make sure the item can only be used once by checking if it was accepted previously.
if item.AcceptedAt != nil || len(item.EncryptedCode) > 0 || item.State != storage.StateActive {
if item.State == storage.StateActive {
item.State = storage.StateDisabled
h.store.WriteTx(storage.CliAuthDatatype, getRealm(r), storage.DefaultUser, name, storage.LatestRev, item, nil, nil)
}
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.Unauthenticated, "login %q has already been accepted by another login flow", name))
return
}
atProto, err := ptypes.TimestampProto(time.Now())
if err != nil {
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.Internal, "login %q cannot generate timestamp: %v", name, err))
return
}
item.AcceptedAt = atProto
nonce := httputils.QueryParam(r, "nonce")
if nonce != "" && nonce != item.Nonce {
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.InvalidArgument, "login failed: nonce mismatch"))
return
}
exp, err := ptypes.Timestamp(item.ExpiresAt)
if err != nil {
exp = time.Unix(0, 0)
}
if time.Now().Sub(exp) > 0 {
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.DeadlineExceeded, "login %q failed: the login state has expired", name))
return
}
code := httputils.QueryParam(r, "code")
if len(code) == 0 {
item.State = storage.StateDisabled
h.store.WriteTx(storage.CliAuthDatatype, getRealm(r), storage.DefaultUser, name, storage.LatestRev, item, nil, nil)
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.InvalidArgument, "login %q failed: no auth code provided", name))
return
}
cryptcode, err := h.crypt.Encrypt(r.Context(), []byte(code), "")
if err != nil {
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.Internal, "cannot generate secret: %v", err))
}
item.EncryptedCode = cryptcode
if err := h.store.WriteTx(storage.CliAuthDatatype, getRealm(r), storage.DefaultUser, name, storage.LatestRev, item, nil, nil); err != nil {
writeAcceptPage(w, h.pageTmpl, h.assetPath, status.Errorf(codes.Unavailable, "write login %q failed: storage is unavailable", name))
return
}
writeAcceptPage(w, h.pageTmpl, h.assetPath, nil)
}