Search code examples
djangopolymerdjango-csrf

polymer-cookie generates duplicate Django csrf cookie


enter image description here

Odd issue I just ran into. Django seems to be generating 2 cookies on when I go to this page. The two cookies doesn't have the same token, so that causes my post to fail because it's validating against the wrong cookie.

Anyone know why this might be happening?

Nothing special in the view:

class ListingDetailView(TemplateView):
    template_name = "bidding/listing_detail.html"

    def get(self, request, *args, **kwargs):
        c = self.get_context_data(**kwargs)
        c['listing'] = get_object_or_404(Listing, id=kwargs['id'])
        return self.render_to_response(c)

... though it seems that the offending code may be inside one of my custom components (when I hide it, the extra cookie ain't generated):

<polymer-element name="bts-place-bid" attributes="href">
    <template>
        <polymer-cookie id="csrfcookie" name="csrftoken"></polymer-cookie>
        <core-ajax
            id="bidxhr"
            method="post"
            body=""
            url="{{ href }}"
            headers='{"X-CSRFToken": "{{ csrftoken }}"}'
            on-core-response="{{ handleResponse }}">
        </core-ajax>

        <core-style ref="bts-place-bid"></core-style>
        <paper-button on-click="{{ toggleDialog }}">Place Bid</paper-button>

        <paper-action-dialog heading="Place Bid"
                      transition="core-transition-center"
                      id="bidDialog">
            <core-style ref="bts-bid-dialog"></core-style>

            <bts-field>
                <label for="id_amount">Bid Amount in Rands</label>
                <input type="number" name="amount" id="id_amount" value="{{ amount }}">
            </bts-field>

            <bts-field vertical layout>
                <label for="id_proposal">Short note or proposal for this bid</label>
                <textarea rows="4" id="id_proposal" name="proposal" value="{{ proposal }}"></textarea>
            </bts-field>

            <paper-button dismissive><core-icon icon="cancel"></core-icon> Cancel</paper-button>
            <paper-button on-click="{{ placeBid }}" affirmative><core-icon icon="note-add"></core-icon> Place Bid!</paper-button>
        </paper-action-dialog>
        <paper-toast id="toastMsg" text=""></paper-toast>

    </template>
    <script>
        Polymer({
            amount: 0,
            proposal: "",
            bidPayload: "",

            ready: function() {
                this.csrftoken = this.$.csrfcookie.value;
            },
            toggleDialog: function(ev, detail, sender) {
                this.$.bidDialog.toggle();
            },
            placeBid: function(ev, detail, sender) {
                this.$.toastMsg.text = "Placing bid, please wait..."
                this.$.toastMsg.show();

                this.$.bidxhr.body = "amount=" + this.amount + "&proposal=" + this.proposal;
                this.$.bidxhr.go();
            },
            handleResponse: function(ev, detail, sender) {
                this.$.toastMsg.text = "Bid placed, refreshing...";
                this.$.toastMsg.show();
                // Auto refresh the page...
                window.location = window.location;
            }
        });
    </script>
</polymer-element>

I suspect the issue may be with <polymer-cookie>, I'll dig around it's source a bit.


Solution

  • In the end the solution was quite simple. Should have thought about it earlier.

    I simply modified my custom component to accept a extra attribute called csrftoken, and to this attribute I can just pass the django {{ csrf_token }} variable. No need to look up the cookie, no need to add a extra input field etc.

    Here is the final component:

    <polymer-element name="bts-place-bid" attributes="href csrftoken">
        <template>
            <core-ajax
                id="bidxhr"
                method="post"
                body=""
                url="{{ href }}"
                headers='{"X-CSRFToken": "{{ csrftoken }}"}'
                on-core-response="{{ handleResponse }}">
            </core-ajax>
    
            <core-style ref="bts-place-bid"></core-style>
            <paper-button on-click="{{ toggleDialog }}">Place Bid</paper-button>
    
            <paper-action-dialog heading="Place Bid"
                          transition="core-transition-center"
                          id="bidDialog">
                <core-style ref="bts-bid-dialog"></core-style>
    
                <bts-field>
                    <label for="id_amount">Bid Amount in Rands</label>
                    <input type="number" name="amount" id="id_amount" value="{{ amount }}">
                </bts-field>
    
                <bts-field vertical layout>
                    <label for="id_proposal">Short note or proposal for this bid</label>
                    <textarea rows="4" id="id_proposal" name="proposal" value="{{ proposal }}"></textarea>
                </bts-field>
    
                <paper-button dismissive><core-icon icon="cancel"></core-icon> Cancel</paper-button>
                <paper-button on-click="{{ placeBid }}" affirmative><core-icon icon="note-add"></core-icon> Place Bid!</paper-button>
            </paper-action-dialog>
            <paper-toast id="toastMsg" text=""></paper-toast>
    
        </template>
        <script>
            Polymer({
                amount: 0,
                proposal: "",
                bidPayload: "",
    
                toggleDialog: function(ev, detail, sender) {
                    this.$.bidDialog.toggle();
                },
                placeBid: function(ev, detail, sender) {
                    this.$.toastMsg.text = "Placing bid, please wait..."
                    this.$.toastMsg.show();
    
                    this.$.bidxhr.body = "amount=" + this.amount + "&proposal=" + this.proposal;
                    this.$.bidxhr.go();
                },
                handleResponse: function(ev, detail, sender) {
                    this.$.toastMsg.text = "Bid placed, refreshing...";
                    this.$.toastMsg.show();
                    // Auto refresh the page...
                    window.location = window.location;
                }
            });
        </script>
    </polymer-element>
    

    ... and this is then used in the main django template as such:

    <bts-place-bid href="{% url 'bidding_listing_bid' id=listing.id %}"
                   csrftoken="{{ csrf_token }}"></bts-place-bid>