Problems, when trying to use the Customer Account API

LasseC
Shopify Partner
1 0 0

Hello,

i am having trouble following the docs of the Customer Account API Customer Account API reference (shopify.dev).

My objective (correct me, if i misunderstood anything, and the objective seems odd or even impossible):
- Setting up the Headless sales channel and using the Customer Account API as a confidential client (i have set up a new shop for testing)

- Authenticating customers to the shopify backend with my Next.js app, by using the code from the docs above (i am not using Hydrogen, since i have to use Next.js for other purposes anyway)


So far, i haven't got any further than completing the authorization process. When trying to obtain the access_token, i recieve the response code 401, "invalid_client", with the following error description:

'The client identifier provided is invalid, the client failed to authenticate, the client did not include its credentials, provided multiple client credentials, or used unsupported credentials type.'

 

Obviously, i triple checked both client-id and client-secret, as well as rotating the credentials in the backend and ensuring the validity of both strings.

Here is the code of my home/login page,

 

import Head from 'next/head'
import Link from 'next/link'
import * as util from '../components/Utilities

export default function Home() {
    function loginCustomer(event) {
        event.preventDefault()

        const clientId = process.env.NEXT_PUBLIC_CLIENT_ID
        const authorizationRequestUrl = new URL(`https://shopify.com/${process.env.NEXT_PUBLIC_SHOP_ID}/auth/oauth/authorize`)
        authorizationRequestUrl.searchParams.append('scope', 'openid email https://api.customers.com/auth/customer.graphql')
        authorizationRequestUrl.searchParams.append('client_id', clientId)
        authorizationRequestUrl.searchParams.append('response_type', 'code')
        authorizationRequestUrl.searchParams.append('redirect_uri', 'http://localhost:3000/dashboard')
        authorizationRequestUrl.searchParams.append('state', util.generateState())
        
        window.location.href = authorizationRequestUrl.toString()
    }

    return (<>
        <Head>
            <meta name="description" content="Beschreibung" />
            <meta name="keywords" content="Keywords" />
            <title>Page title</title>
            <link rel="icon" href="/favicon.png" sizes="any" />
        </Head>

        <main id="page-home">
            <div className="ml-container">
                <form onSubmit={loginCustomer}>
                    <button type="submit">Login</button>
                </form>
            </div>
        </main>
    </>)
}

 

 

the dashboard,

 

import Head from 'next/head'
import Link from 'next/link'
import { useEffect } from 'react'
// schopify
import { getCredentials } from './api/accounts/auth'

export async function getServerSideProps({ query }) {
    // obtain access token
    const credentials = await getCredentials()
    const body = new URLSearchParams()
    body.append('grant_type', 'authorization_code')
    body.append('client_id', process.env.NEXT_PUBLIC_CLIENT_ID)
    body.append('redirect_uri', `http://localhost:3000/dashboard`)
    body.append('code', query.code)

    const headers = {
        'content-type': 'application/x-www-form-urlencoded',
        'Authorization': `Basic ${credentials}`
    }
console.log('Authorization: ', `Basic ${credentials}`);

    const response = await fetch(`https://shopify.com/${process.env.NEXT_PUBLIC_SHOP_ID}/auth/oauth/token`, {
        method: 'POST', headers: headers, body: body,
    })

    const test = await response.json()
// this line logs the error code 400, invalid_client
    console.log(test);
    
    return {
        props: {}
    }
}


export default function Dashboard(props) {
    function logoutCustomer() {
        console.log('logout');
    }

    return (<>
        <Head>
            <meta name="description" content="Beschreibung" />
            <meta name="keywords" content="Keywords" />
            <title>Dashboard</title>
            <link rel="icon" href="/favicon.png" sizes="any" />
        </Head>

        <main id="page-dashboard">
            <div className="ml-container">
                <h1>Dashboard</h1>
                <button onClick={logoutCustomer}>Logout</button>
            </div>
        </main>
    </>)
}

 

 and the "authorization header" functions

 

import crypto from 'crypto'

export async function getCredentials() {
    const clientId = process.env.NEXT_PUBLIC_CLIENT_ID;
    const clientSecret = process.env.NEXT_PUBLIC_CLIENT_SECRET;
    // const credentials = await crypto.subtle.digest({ name: "SHA-256" }, new TextEncoder().encode(`${clientId}:${clientSecret}`))
    // const hash = convertBufferToString(credentials);
    // return base64UrlEncode(hash);
    return await crypto.subtle.digest({ name: "SHA-256" }, new TextEncoder().encode(`${clientId}:${clientSecret}`))
}

function base64UrlEncode(string) {
    const base64 = btoa(string);
    // This is to ensure that the encoding does not have +, /, or = characters in it.
    return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}

function convertBufferToString(ArrayBuffer) {
    const uintArray = new Uint8Array(ArrayBuffer);
    const numberArray = Array.from(uintArray);
    return String.fromCharCode(...numberArray);
}

 

Note, that i tried converting the arrayBuffer back to a string as well, since it didn't seem right to me in the documentation.

I know that the Customer Account API is brand new, but since i don't see any alternative in the docs, i have to keep trying for now. Any help would be well apreciated.

Thanks in advance and
best regards

Reply 1 (1)
KevweAmena
Shopify Partner
1 0 0

I'm facing a similar issue, doesn't seem to have any other alternative in the docs either