func()

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)
}