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