All things Shopify and commerce
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
I'm facing a similar issue, doesn't seem to have any other alternative in the docs either
User | RANK |
---|---|
44 | |
40 | |
24 | |
24 | |
21 |
Make the shift from discounts to donations, and witness your business not only thrive fina...
By Holly Dec 4, 2023On our Shopify Expert Marketplace, you can find many trusted third party developers and fr...
By Arno Nov 27, 2023You've downloaded the Search & Discovery app from the Shopify App store, and as you're ...
By Skye Nov 8, 2023