Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.0alpha] send login (=customerAccessToken) to checkout page #430

Closed
formatlos opened this issue Oct 2, 2017 · 14 comments
Closed

[1.0alpha] send login (=customerAccessToken) to checkout page #430

formatlos opened this issue Oct 2, 2017 · 14 comments

Comments

@formatlos
Copy link

This is probably a little off-topic here, but as the next version of the js-buy-sdk internally also uses the graphql endpoints I thought I might at least find someone who has some more insights in the internals. I already posted the problem in the official forum, but even after a few weeks nobody answered my question.

How can I send the customerAccessToken I get with logging in via API (=customerAccessTokenCreate) to the checkout page to log the user in?

As I'm building a completely custom storefront, logging in again via the 'normal' Shopify login form is not an option.

Is there some hidden query param I have to send or is this something which is not possible at all? I'd really appreciate talking to an actual developer at shopify because all the so called experts and the forum is not very helpful.

@mtaher
Copy link

mtaher commented Oct 2, 2017

Hi @formatlos, if you append customer_access_token as a query string (for example, ?customer_access_token=9f51f1df96ce836b929e42cb14c15260), it will login the user.

We're making some changes to webUrl property for checkouts to include this value, stay tuned.

@formatlos
Copy link
Author

Oh thanks for the fast reply, that sounds great. The Login-Button seems to be gone already. When do you think is the customer_access_token query string ready for usage?

@mtaher
Copy link

mtaher commented Oct 2, 2017

The checkout page already consumes the customer_access_token query string if it is present.

We'll make the change for the webUrl to auto include the access token fairly soon but we don't have an exact timeline just yet.

@formatlos
Copy link
Author

Doesn't seem to work for my shop. Does this only work for new shops?

@mtaher
Copy link

mtaher commented Oct 2, 2017

Under Settings > Checkouts in your Shopify shop, do you have customer accounts enabled?

screen shot 2017-10-02 at 11 12 01 am

@formatlos
Copy link
Author

Ah sorry, I disabled it for testing something and forgot to turn it back on again. Thanks a lot, you're awesome

@mtaher
Copy link

mtaher commented Oct 2, 2017

Glad it's all sorted out :).

@naiduasn
Copy link

@formatlos is this sorted out for you?
@mtaher can you check issue #561 and let me know what am I missing there? I am passing customer token but still landing on signin screen

@formatlos
Copy link
Author

@naiduasn yes it did work in the end, not sure if that's still the case though.

@gil--
Copy link
Member

gil-- commented Oct 31, 2018

@naiduasn @formatlos I haven't been able to get it working, I think it may have been deprecated? assigned the checkout instance to the customer and passed same token through to the weburl but login still shows.

@rebeccajfriedman
Copy link
Contributor

The use of customer_access_token is no longer supported, as it was a security vulnerability. Passing sensitive login data via a query param is unsafe.

@darkguy2008
Copy link

darkguy2008 commented Oct 13, 2020

The use of customer_access_token is no longer supported, as it was a security vulnerability. Passing sensitive login data via a query param is unsafe.

I disagree. Sensitive data is a Secret, something that should be on the server side. Once you log in (in any site, mind you), you receive an access token (that's how JWT works, basically) therefore it stops being sensitive, as it's already in the user's control (on their browser). I don't see how passing a token that you already have is a security vulnerability. How is JWT secure, then?

Even better, how do you consider the Storefront API being secure if it also uses a token that's on the client side? Receiving a token from a request and having a token in the source files received by the server is the same thing, you just reach one from Chrome's Network tab and the other one through the Sources tab.

I really fail to see the logic behind this reasoning.

@Zavtramen
Copy link

Zavtramen commented Sep 6, 2022

For headless frontend this can be done only one way (for September 2022) - using Multipass.
Official documentation is not very good - it have some blind spots about exact implementation in case of headless frontend.
So, I've decided to post my variant for anyone who are looking for solution (keep in mind, that my solution can be buggy or so).

The task is: we have a separate frontend app which communicates with Shopify through Storefront API and allows users to select goods and place them into the cart with API calls to mutations cartCreate/cartLinesAdd/cartLinesUpdate (we are using cart object, not checkout object).
Cart object has a checkoutUrl field, and to complete the checkout user goes to this url on shopify side (and sees his cart content there).
Users can log in on our site using customerAccessTokenCreate mutation. So we can show his orders history and edit his shipping addresses.
The problem is that than user goes to checkoutUrl shopify asks user to Log In there, and we need a solution to pass our login to Shopify.

The solution:
We need to use Multipass feature of Shopify (sad, but this is only for Plus accounts).
So on frontend we need to get a special url for checkout which will automatically login us on shopify and redirects to checkout page with logged in user.

So, this is part of frontend to get special checkoutUrl:

// This code is only showing the concept, it wouldn't work being copied as-is (fetchData is out inner method, based on native js fetch)
async updateCheckoutUrl() {
    if (!this.cart?.checkoutUrl) {
        this.checkoutUrl = '';
        return;
    }

    if (this.customerAccessToken) {
        const data = await fetchData({
            url: '/api/admin/multipass/',
            method: 'POST',
            data: {
                return_to: this.cart.checkoutUrl,
                customerAccessToken: this.customerAccessToken
            },
            $config: this.$config
        });
        this.checkoutUrl = data?.url || this.cart.checkoutUrl;
    } else {
        this.checkoutUrl = this.cart.checkoutUrl;
    }
}

And this is a part of our backend (nodejs + express), using multipassify npm package (recommended by Shopify: https://shopify.dev/api/multipass#example-implementation):
You need to enable multipass in Shopify panel first (https://shopify.dev/api/multipass#1-enable-multipass-login-in-the-shopify-admin) and get secret key (SHOPIFY_MULTIPASS_TOKEN here).

app.post('/api/admin/multipass/', (request, response) => {
    axios
        .post(`https://${process.env.SHOPIFY_DOMAIN}/api/2022-07/graphql.json`, {
            query: `#graphql
                query($customerAccessToken: String!) {
                    customer(customerAccessToken: $customerAccessToken) {
                        id
                        email
                       }
                }`,
            variables: {
                "customerAccessToken": request.body?.customerAccessToken
            }
        }, {
            headers: {
                'Content-Type': 'application/json',
                'X-Shopify-Storefront-Access-Token': process.env.SHOPIFY_STOREFRONT_TOKEN
            }
        })
        .then(res => {
            const email = res?.data?.data?.customer?.email;

            if (!email) {
                throw new Error('Invalid auth');
            }

            const Multipassify = require('multipassify');

            // Construct the Multipassify encoder
            const multipassify = new Multipassify(process.env.SHOPIFY_MULTIPASS_TOKEN);

            // Create your customer data hash
            const customerData = { email: email, remote_ip: 'USE_CLIENT_IP_HERE',  return_to: request.body?.return_to};

            // Encode a Multipass token
            const token = multipassify.encode(customerData);

            // Generate a Shopify multipass URL to the shop
            const url = multipassify.generateUrl(customerData, process.env.SHOPIFY_DOMAIN);

            response.json({url})
        })
        .catch(err => response.json({ error: err.message }))
});

Hope, this will help someone looking for solution.

@KhangNguyen303
Copy link

For headless frontend this can be done only one way (for September 2022) - using Multipass. Official documentation is not very good - it have some blind spots about exact implementation in case of headless frontend. So, I've decided to post my variant for anyone who are looking for solution (keep in mind, that my solution can be buggy or so).

The task is: we have a separate frontend app which communicates with Shopify through Storefront API and allows users to select goods and place them into the cart with API calls to mutations cartCreate/cartLinesAdd/cartLinesUpdate (we are using cart object, not checkout object). Cart object has a checkoutUrl field, and to complete the checkout user goes to this url on shopify side (and sees his cart content there). Users can log in on our site using customerAccessTokenCreate mutation. So we can show his orders history and edit his shipping addresses. The problem is that than user goes to checkoutUrl shopify asks user to Log In there, and we need a solution to pass our login to Shopify.

The solution: We need to use Multipass feature of Shopify (sad, but this is only for Plus accounts). So on frontend we need to get a special url for checkout which will automatically login us on shopify and redirects to checkout page with logged in user.

So, this is part of frontend to get special checkoutUrl:

// This code is only showing the concept, it wouldn't work being copied as-is (fetchData is out inner method, based on native js fetch)
async updateCheckoutUrl() {
    if (!this.cart?.checkoutUrl) {
        this.checkoutUrl = '';
        return;
    }

    if (this.customerAccessToken) {
        const data = await fetchData({
            url: '/api/admin/multipass/',
            method: 'POST',
            data: {
                return_to: this.cart.checkoutUrl,
                customerAccessToken: this.customerAccessToken
            },
            $config: this.$config
        });
        this.checkoutUrl = data?.url || this.cart.checkoutUrl;
    } else {
        this.checkoutUrl = this.cart.checkoutUrl;
    }
}

And this is a part of our backend (nodejs + express), using multipassify npm package (recommended by Shopify: https://shopify.dev/api/multipass#example-implementation): You need to enable multipass in Shopify panel first (https://shopify.dev/api/multipass#1-enable-multipass-login-in-the-shopify-admin) and get secret key (SHOPIFY_MULTIPASS_TOKEN here).

app.post('/api/admin/multipass/', (request, response) => {
    axios
        .post(`https://${process.env.SHOPIFY_DOMAIN}/api/2022-07/graphql.json`, {
            query: `#graphql
                query($customerAccessToken: String!) {
                    customer(customerAccessToken: $customerAccessToken) {
                        id
                        email
                       }
                }`,
            variables: {
                "customerAccessToken": request.body?.customerAccessToken
            }
        }, {
            headers: {
                'Content-Type': 'application/json',
                'X-Shopify-Storefront-Access-Token': process.env.SHOPIFY_STOREFRONT_TOKEN
            }
        })
        .then(res => {
            const email = res?.data?.data?.customer?.email;

            if (!email) {
                throw new Error('Invalid auth');
            }

            const Multipassify = require('multipassify');

            // Construct the Multipassify encoder
            const multipassify = new Multipassify(process.env.SHOPIFY_MULTIPASS_TOKEN);

            // Create your customer data hash
            const customerData = { email: email, remote_ip: 'USE_CLIENT_IP_HERE',  return_to: request.body?.return_to};

            // Encode a Multipass token
            const token = multipassify.encode(customerData);

            // Generate a Shopify multipass URL to the shop
            const url = multipassify.generateUrl(customerData, process.env.SHOPIFY_DOMAIN);

            response.json({url})
        })
        .catch(err => response.json({ error: err.message }))
});

Hope, this will help someone looking for solution.

Yo thank you so much bro. That will works with new Remix Hydrogen with some customize

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants