[{"data":1,"prerenderedAt":631},["ShallowReactive",2],{"bootstrap":3,"blog-single-sign-on-with-commerce-layer-using-next-js-and-auth0":277},{"navigation":4,"globalConfig":196,"forms":218},{"headerSections":5,"headerFooterLinks":67,"footer":76,"legalLinks":153,"socialLinks":176},[6,39,44,50,57,62],{"hasSubMenu":7,"title":8,"links":9,"columns":38},true,"Product",[10,20,26,32],{"showTag":11,"_modelApiKey":12,"text":13,"subtitle":14,"icon":15,"isExternal":11,"link":16,"openForm":11},false,"navigation_link","Universal checkout","Sell anywhere","globe-simple",{"_modelApiKey":17,"slug":18,"title":19},"page","universal-checkout","Universal Checkout",{"showTag":11,"_modelApiKey":12,"text":21,"subtitle":22,"icon":23,"isExternal":11,"link":24,"openForm":11},"Distributed OMS","Manage orders","package",{"_modelApiKey":17,"slug":25,"title":21},"distributed-oms",{"showTag":11,"_modelApiKey":12,"text":27,"subtitle":28,"icon":29,"isExternal":11,"link":30,"openForm":11},"Promotion engine","Automated discounts","seal-percent",{"_modelApiKey":17,"slug":31,"title":27},"promotion-engine",{"showTag":11,"_modelApiKey":12,"text":33,"subtitle":34,"icon":35,"isExternal":11,"link":36,"openForm":11},"POS system","Operate stores","devices",{"_modelApiKey":17,"slug":37,"title":33},"pos-system",2,{"hasSubMenu":11,"showTag":11,"_modelApiKey":12,"text":40,"isExternal":11,"link":41,"openForm":11,"title":40},"Pricing",{"_modelApiKey":42,"slug":43},"pricing_page","pricing",{"hasSubMenu":11,"showTag":11,"_modelApiKey":12,"text":45,"isExternal":11,"link":46,"openForm":11,"title":45},"Customers",{"_modelApiKey":47,"slug":48,"title":49},"customer_landing_page","customers","Customers page",{"hasSubMenu":11,"showTag":11,"_modelApiKey":12,"text":51,"isExternal":11,"link":52,"openForm":11,"form":54,"title":51},"Partners",{"_modelApiKey":17,"slug":53,"title":51},"partners",{"_modelApiKey":55,"slug":53,"name":56},"form","PartnersForm",{"hasSubMenu":11,"showTag":11,"_modelApiKey":12,"text":58,"label":59,"isExternal":7,"url":60,"openForm":11,"title":61},"Docs Hub","Learn more","https:\u002F\u002Fdocs.commercelayer.io\u002F","Docs",{"hasSubMenu":11,"showTag":11,"_modelApiKey":12,"text":63,"isExternal":11,"link":64,"openForm":11,"title":63},"Blog",{"_modelApiKey":65,"slug":66},"blog_landing_page","blog",[68,72],{"text":69,"link":70},"Sign in",{"_modelApiKey":12,"text":69,"isExternal":7,"url":71,"openForm":11},"https:\u002F\u002Fdashboard.commercelayer.io\u002Fsign_in",{"text":73,"link":74},"Sign up for free",{"_modelApiKey":12,"text":73,"isExternal":7,"url":75,"openForm":11},"https:\u002F\u002Fdashboard.commercelayer.io\u002Fsign_up",[77,89,109,132],{"hasSubMenu":7,"title":8,"links":78},[79,81,83,85,87],{"showTag":11,"_modelApiKey":12,"text":13,"subtitle":14,"icon":15,"isExternal":11,"link":80,"openForm":11},{"_modelApiKey":17,"slug":18,"title":19},{"showTag":11,"_modelApiKey":12,"text":21,"subtitle":22,"icon":23,"isExternal":11,"link":82,"openForm":11},{"_modelApiKey":17,"slug":25,"title":21},{"showTag":11,"_modelApiKey":12,"text":27,"subtitle":28,"icon":29,"isExternal":11,"link":84,"openForm":11},{"_modelApiKey":17,"slug":31,"title":27},{"showTag":11,"_modelApiKey":12,"text":33,"subtitle":34,"icon":35,"isExternal":11,"link":86,"openForm":11},{"_modelApiKey":17,"slug":37,"title":33},{"showTag":11,"_modelApiKey":12,"text":40,"isExternal":11,"link":88,"openForm":11},{"_modelApiKey":42,"slug":43},{"hasSubMenu":7,"title":90,"links":91},"Resources",[92,94,99,104,107],{"showTag":11,"_modelApiKey":12,"text":93,"isExternal":7,"url":60,"openForm":11},"Documentation",{"showTag":11,"_modelApiKey":12,"text":95,"label":95,"isExternal":11,"link":96,"openForm":11},"Agentic Commerce",{"_modelApiKey":17,"slug":97,"title":98},"agentic-commerce-whitepaper","Agentic Commerce Whitepaper",{"showTag":11,"_modelApiKey":12,"text":100,"isExternal":11,"link":101,"openForm":11},"Core concepts",{"_modelApiKey":17,"slug":102,"title":103},"headless-commerce","Headless commerce",{"showTag":11,"_modelApiKey":12,"text":105,"isExternal":7,"url":106,"openForm":11},"Changelog","https:\u002F\u002Fdocs.commercelayer.io\u002Fchangelog\u002F",{"showTag":11,"_modelApiKey":12,"text":63,"isExternal":11,"link":108,"openForm":11},{"_modelApiKey":65,"slug":66},{"hasSubMenu":7,"title":110,"links":111},"Company",[112,116,118,121,126],{"showTag":11,"_modelApiKey":12,"text":113,"isExternal":11,"link":114,"openForm":11},"Vision",{"_modelApiKey":17,"slug":115,"title":113},"vision",{"showTag":11,"_modelApiKey":12,"text":45,"isExternal":11,"link":117,"openForm":11},{"_modelApiKey":47,"slug":48,"title":49},{"showTag":11,"_modelApiKey":12,"text":51,"isExternal":11,"link":119,"openForm":11,"form":120},{"_modelApiKey":17,"slug":53,"title":51},{"_modelApiKey":55,"slug":53,"name":56},{"showTag":11,"_modelApiKey":12,"text":122,"isExternal":11,"link":123,"openForm":11},"Security",{"_modelApiKey":17,"slug":124,"title":125},"security","Security at Commerce Layer",{"showTag":11,"_modelApiKey":12,"text":127,"label":128,"isExternal":11,"openForm":7,"form":129},"Giveback","Giveback program",{"_modelApiKey":55,"slug":130,"name":131},"giveback","GivebackForm",{"hasSubMenu":7,"title":133,"links":134},"Get in touch",[135,140,145,149],{"showTag":11,"_modelApiKey":12,"text":136,"isExternal":11,"openForm":7,"form":137},"Contact",{"_modelApiKey":55,"slug":138,"name":139},"contact","ContactUsForm",{"showTag":11,"_modelApiKey":12,"text":141,"isExternal":11,"openForm":7,"form":142},"Book a demo",{"_modelApiKey":55,"slug":143,"name":144},"demo","RequestDemoForm",{"showTag":11,"_modelApiKey":12,"text":146,"label":147,"isExternal":7,"url":148,"openForm":11},"Support","Email support","mailto:support@commercelayer.io",{"showTag":11,"_modelApiKey":12,"text":150,"label":151,"isExternal":7,"url":152,"openForm":11},"Community","Discord community","https:\u002F\u002Fdiscord.gg\u002Fcommercelayer",{"hasSubMenu":7,"title":154,"links":155},"Legal",[156,161,165,169,173],{"showTag":11,"_modelApiKey":12,"text":157,"label":158,"isExternal":7,"url":159,"openForm":11,"htmlClassNames":160},"Terms","Terms and conditions","\u002Flegal-embed\u002Fterms-of-service","iubenda-nostyle no-brand iubenda-noiframe iubenda-embed",{"showTag":11,"_modelApiKey":12,"text":162,"label":163,"isExternal":7,"url":164,"openForm":11,"htmlClassNames":160},"Privacy","Privacy policy","\u002Flegal-embed\u002Fprivacy-policy",{"showTag":11,"_modelApiKey":12,"text":166,"label":167,"isExternal":7,"url":168,"openForm":11,"htmlClassNames":160},"Cookies","Cookie policy","\u002Flegal-embed\u002Fcookie-policy",{"showTag":11,"_modelApiKey":12,"text":170,"isExternal":7,"url":171,"openForm":11,"htmlClassNames":172},"Notice at collection","#","iubenda-cs-uspr-link",{"showTag":11,"_modelApiKey":12,"text":174,"isExternal":7,"url":171,"openForm":11,"htmlClassNames":175},"Privacy preferences","iubenda-cs-preferences-link",[177,180,184,188,192],{"title":178,"type":179,"url":152,"isExternal":7},"Discord","discord-logo",{"title":181,"type":182,"url":183,"isExternal":7},"Github","github","https:\u002F\u002Fgithub.com\u002Fcommercelayer",{"title":185,"type":186,"url":187,"isExternal":7},"Bluesky","bluesky-logo","https:\u002F\u002Fbsky.app\u002Fprofile\u002Fcommercelayer.io",{"title":189,"type":190,"url":191,"isExternal":7},"X","x-logo","https:\u002F\u002Fx.com\u002Fcommercelayer",{"title":193,"type":194,"url":195,"isExternal":7},"LinkedIn","linkedin","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fcommerce-layer",{"pagesWithoutCtaPanel":197,"ctaPanel":198,"notification":208,"showApiResponseTime":11,"newsletterSignup":216},[],{"title":199,"subtitle":200,"ctas":201},"Ready to get started?","Discover how we can help your business.",[202,205],{"_modelApiKey":203,"text":204,"isExternal":7,"url":75,"openForm":11},"link","Get started for free",{"_modelApiKey":203,"text":206,"isExternal":11,"openForm":7,"form":207},"Contact us",{"_modelApiKey":55,"slug":138,"name":139},{"showNotification":7,"link":209,"linkText":210,"text":215},{"_modelApiKey":203,"text":210,"isExternal":11,"link":211,"openForm":11},"Read article",{"_modelApiKey":212,"slug":213,"title":214},"blog_post","payment-api-redesigned","Introducing our new Payments API. For humans and agents.","\u003Cp>Introducing our new Payments API. For humans and agents.\u003C\u002Fp>\n",{"ctaText":217},"Subscribe",[219,225,229,236,240,246,249,253,261,262,267,272,274],{"_modelApiKey":55,"slug":220,"pardotFormEmbedCode":221,"formEmbedMinHeight":222,"successTitle":223,"successCopy":224},"agentic-commerce","https:\u002F\u002Fcommercelayer.fillout.com\u002Ft\u002F5w4Khwgxr6us?whitepaper=agentic",520,"Thank you for your request!","We’ll be in touch soon to arrange a date and time for your personalized demo.",{"_modelApiKey":55,"slug":226,"pardotFormEmbedCode":227,"formEmbedMinHeight":228,"successTitle":223,"successCopy":224},"shopify","https:\u002F\u002Fform.fillout.com\u002Ft\u002FgVnLSmKZwRus",850,{"_modelApiKey":55,"slug":130,"pardotFormEmbedCode":230,"formEmbedMinHeight":231,"displayTitle":232,"copy":233,"successTitle":234,"successCopy":235},"https:\u002F\u002Fform.fillout.com\u002Ft\u002FmVDF8aAiofus",500,"We give back.","\u003Cp>Part of Commerce Layer&#39;s company ethos is to empathize and support one another: co-workers, customers, and community. We want to do our part to help nonprofit organizations and small businesses who may feel adversely affected by the current global economic climate, and as a result, need in-kind support to reach their customers.\u003C\u002Fp>\n\n\u003Cp>If this general description matches your organization’s needs, please tell us your story by completing the following form so we can consider helping your cause.\u003C\u002Fp>\n","Thank you!","We will contact you soon to learn more about your story.",{"_modelApiKey":55,"slug":237,"pardotFormEmbedCode":227,"formEmbedMinHeight":228,"displayTitle":238,"copy":239},"book-an-onboarding-session","Book an onboarding session","\u003Cp>Schedule some time and our devs will help you set up a Commerce Layer + Sanity store.\u003C\u002Fp>\n",{"_modelApiKey":55,"slug":241,"pardotFormEmbedCode":242,"formEmbedMinHeight":243,"displayTitle":244,"successTitle":234,"successCopy":245},"download-our-whitepaper","https:\u002F\u002Fform.fillout.com\u002Ft\u002F5YPWmy8oT7us?whitepaper=ComposableCommerce",450,"Download our whitepaper.","Check your inbox. You should receive a download link soon.",{"_modelApiKey":55,"slug":247,"pardotFormEmbedCode":248,"formEmbedMinHeight":243,"displayTitle":244,"successTitle":234,"successCopy":245},"download-the-content-and-commerce-whitepaper","https:\u002F\u002Fform.fillout.com\u002Ft\u002F5YPWmy8oT7us?whitepaper=ContentAndCommerce",{"_modelApiKey":55,"slug":250,"pardotFormEmbedCode":251,"formEmbedMinHeight":243,"displayTitle":244,"copy":252,"successTitle":234,"successCopy":245},"whitepaper","https:\u002F\u002Fform.fillout.com\u002Ft\u002F5YPWmy8oT7us?whitepaper=Jamstack","\u003Cp>Commerce Layer is the perfect platform for building commerce on the Jamstack. Convert any design into pixel perfect HTML pages. Connect a best-of-breed headless CMS. Go serverless and forget the hassle of managing complex infrastructures.\u003C\u002Fp>\n",{"_modelApiKey":55,"slug":254,"pardotFormEmbedCode":255,"formEmbedMinHeight":256,"displayTitle":257,"copy":258,"successTitle":259,"successCopy":260},"contact-links","https:\u002F\u002Fform.fillout.com\u002Ft\u002FeWPyhHGHT7us",750,"Contact us.","\u003Cp>Tell us about your project. Our team will reach out to you for a personalized offer.\u003C\u002Fp>\n","Thanks for your message!","We’ll be in touch soon to discuss your project details.",{"_modelApiKey":55,"slug":138,"pardotFormEmbedCode":255,"formEmbedMinHeight":256,"displayTitle":257,"copy":258,"successTitle":259,"successCopy":260},{"_modelApiKey":55,"slug":263,"pardotFormEmbedCode":227,"formEmbedMinHeight":228,"displayTitle":264,"successTitle":265,"successCopy":266},"demo-embed","Want to see it in action? Request a demo.","Thanks for your request!","We’ll be in touch soon to arrange a date and time for your free live demo.",{"_modelApiKey":55,"slug":53,"pardotFormEmbedCode":268,"formEmbedMinHeight":228,"displayTitle":269,"copy":270,"successTitle":234,"successCopy":271},"https:\u002F\u002Fform.fillout.com\u002Ft\u002F9MTkDWMUSqus","Become our partner.","\u003Cp>Join us and get our guidance in helping your clients succeed. Our partners are our ambassadors. We are committed to your success as much as you are.\u003C\u002Fp>\n","We will contact you soon to discuss the details of our partnership.",{"_modelApiKey":55,"slug":273,"pardotFormEmbedCode":268,"formEmbedMinHeight":228,"displayTitle":269,"copy":270,"successTitle":234,"successCopy":271},"partners-embed",{"_modelApiKey":55,"slug":143,"pardotFormEmbedCode":227,"formEmbedMinHeight":228,"displayTitle":275,"copy":276,"successTitle":265,"successCopy":266},"Get a free live demo.","\u003Cp>Get a personalized demo and learn more about what Commerce Layer can do for your company.\u003C\u002Fp>\n",{"_modelApiKey":212,"link":278,"subtitle":283,"date":283,"author":284,"additionalAuthors":294,"icon":295,"abstract":296,"tag":297,"displayTitle":279,"cardRatio":300,"featured":11,"seoMetaTags":301,"layout":365,"blogPostCarousel":303,"_seoMetaTags":592,"authors":626},{"text":279,"isExternal":11,"url":280,"link":281},"Single sign-on with Commerce Layer using Next.js and Auth0.","",{"_modelApiKey":212,"slug":282},"single-sign-on-with-commerce-layer-using-next-js-and-auth0","October 10, 2025",{"name":285,"picture":286},"Matteo Alessani",{"url":287,"alt":288,"title":288,"width":289,"height":289,"format":290,"focalPoint":291,"customData":293},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1754320247-matteo-a.jpg","Commerce Layer avatar",1280,"jpg",{"x":292,"y":292},0.5,{},[],"lock-key","Learn how to give your customers a way to be authenticated so that they can access to a dedicated set of features such as saved addresses and payment sources, order history, and more.",{"color":298,"name":299},"gray","Tutorials","portrait",[302,306,310,313,317,320,323,327,331,335,338,342,346,350,354,357,361],{"attributes":303,"tag":304,"content":305},null,"title","Single Sign-On with Commerce Layer using Next.js and Auth0",{"attributes":307,"tag":309,"content":303},{"property":308,"content":305},"og:title","meta",{"attributes":311,"tag":309,"content":303},{"name":312,"content":305},"twitter:title",{"attributes":314,"tag":309,"content":303},{"name":315,"content":316},"description","A dive deep into how easy it is to integrate Commerce Layer with Auth0, one of the most famous identity providers, using Next.js.",{"attributes":318,"tag":309,"content":303},{"property":319,"content":316},"og:description",{"attributes":321,"tag":309,"content":303},{"name":322,"content":316},"twitter:description",{"attributes":324,"tag":309,"content":303},{"property":325,"content":326},"og:image","https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1759940128-auth0-sso-blog-post-thumb.jpg?auto=format&fit=max&w=1200",{"attributes":328,"tag":309,"content":303},{"property":329,"content":330},"og:image:width","1200",{"attributes":332,"tag":309,"content":303},{"property":333,"content":334},"og:image:height","630",{"attributes":336,"tag":309,"content":303},{"name":337,"content":326},"twitter:image",{"attributes":339,"tag":309,"content":303},{"property":340,"content":341},"og:locale","en",{"attributes":343,"tag":309,"content":303},{"property":344,"content":345},"og:type","article",{"attributes":347,"tag":309,"content":303},{"property":348,"content":349},"og:site_name","Commerce Layer",{"attributes":351,"tag":309,"content":303},{"property":352,"content":353},"article:modified_time","2025-10-10T14:04:21Z",{"attributes":355,"tag":309,"content":303},{"property":356,"content":280},"article:publisher",{"attributes":358,"tag":309,"content":303},{"name":359,"content":360},"twitter:card","summary_large_image",{"attributes":362,"tag":309,"content":303},{"name":363,"content":364},"twitter:site","@commercelayer",[366,371,376,380,391,394,397,402,405,413,418,421,424,427,430,433,440,448,451,453,456,463,466,468,475,477,480,482,485,487,494,496,499,501,504,506,513,515,518,520,522,525,532,534,537,539,542,544,547,549,552,554,557,564,571,573,576,578,581,584,586,589],{"_modelApiKey":367,"icon":368,"copy":369,"color":370},"banner","info","\u003Cp>This article was originally written on March 24, 2023. It has been recently updated to improve authentication handling with the \u003Ccode>js-auth\u003C\u002Fcode> library \u003Ccode>v7\u003C\u002Fcode>, which enhances token management. We&#39;ve also updated the example to use the latest Auth0 packages based on the Next.js app router.\u003C\u002Fp>\n","red",{"_modelApiKey":372,"copy":373,"listStyle":374,"darkTheme":11,"buttons":375,"color":370,"indent":7},"wysiwyg","\u003Cp>In \u003Ca href=\"\u002Fblog\u002Freasons-to-build-authentication-into-your-ecommerce-experience\">one of our previous articles\u003C\u002Fa>, we explained the benefits that an authenticated ecommerce experience can provide with a personalized set of features, like saved addresses and payment sources, order history, dedicated markets and price lists, and so on.\u003C\u002Fp>\n\n\u003Cp>This is a dive deep into how easy it is to integrate Commerce Layer together with \u003Ca href=\"https:\u002F\u002Fauth0.com\u002F\">Auth0\u003C\u002Fa>, one of the most famous identity providers, using \u003Ca href=\"https:\u002F\u002Fnextjs.org\u002F\">Next.js\u003C\u002Fa>. During this tutorial, we will create a project where:\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>An unauthenticated user (aka an anonymous visitor to your site) can see a product with a price as a guest.\u003C\u002Fli>\n\u003Cli>A signup or sign-in leads to an authenticated customer’s order history.\u003C\u002Fli>\n\u003C\u002Ful>\n","checkbox",[],{"_modelApiKey":372,"copy":377,"listStyle":378,"darkTheme":11,"buttons":379,"color":370,"indent":11},"\u003Ch3>Prerequisites\u003C\u002Fh3>\n\n\u003Cul>\n\u003Cli>A general understanding of how APIs and the command-line work.\u003C\u002Fli>\n\u003Cli>Basic HTML, CSS, and JavaScript knowledge.\u003C\u002Fli>\n\u003Cli>A \u003Ca href=\"https:\u002F\u002Fdashboard.commercelayer.io\u002Fsign_up\">Commerce Layer\u003C\u002Fa> \u003Cstrong>enterprise\u003C\u002Fstrong> account or planning to move to one.\u003C\u002Fli>\n\u003Cli>An organization where you have created the required commerce data resources for your market (you can follow the \u003Ca href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fcore\u002Fonboarding\u002Fguided-setup\">guided setup\u003C\u002Fa> or \u003Ca href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fcore\u002Fonboarding\u002Fmanual-configuration\">manual configuration\u003C\u002Fa> to achieve this).\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Before we get started, let&#39;s look at a sequence diagram that explains what we hope to achieve (take some time reviewing this because we work on building all of it with this proof of concept):\u003C\u002Fp>\n","directional",[],{"_modelApiKey":381,"border":7,"caption":382,"image":383,"size":390,"darkTheme":11,"rounded":7,"color":370},"image_block","Commerce Layer's authentication sequence",{"url":384,"alt":385,"title":385,"width":386,"height":387,"format":290,"focalPoint":388,"customData":389},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1759744737-sso-diagram.jpg","SSO diagram",1920,2336,{"x":292,"y":292},{},"standard",{"_modelApiKey":372,"copy":392,"listStyle":378,"darkTheme":11,"buttons":393,"color":370,"indent":11},"\u003Col>\n\u003Cli>User clicks on the login button.\u003C\u002Fli>\n\u003Cli>The Next.js endpoint (\u003Ccode>\u002Fauth\u002Flogin\u003C\u002Fcode>) redirects to the Auth0 Login Dialog.\u003C\u002Fli>\n\u003Cli>After successful authentication, Auth0 calls the \u003Ccode>\u002Fauth\u002Fcallback\u003C\u002Fcode> endpoint with a code.\u003C\u002Fli>\n\u003Cli>The Next.js endpoint exchanges the code with Auth0.\u003C\u002Fli>\n\u003Cli>Auth0 replies with an access token and an ID token.\u003C\u002Fli>\n\u003Cli>Using a Commerce Layer integration application, the customer is retrieved or created on Commerce Layer using their email.\u003C\u002Fli>\n\u003Cli>The customer ID is received from Commerce Layer.\u003C\u002Fli>\n\u003Cli>The user metadata on Auth0 is updated with the customer ID provided by Commerce Layer using an Auth0 M2M application.\u003C\u002Fli>\n\u003Cli>An Auth0 session is created on the browser.\u003C\u002Fli>\n\u003Cli>The protected endpoint \u003Ccode>\u002Fapi\u002Ftoken\u003C\u002Fcode> can now be called as the user is logged in with Auth0.\u003C\u002Fli>\n\u003Cli>The customer ID is retrieved from the user&#39;s metadata on Auth0 using an Auth0 M2M application and is used to get the customer token with the JWT bearer flow.\u003C\u002Fli>\n\u003Cli>The customer token is retrieved on the frontend, allowing the user to interact with Commerce Layer APIs as the customer.\u003C\u002Fli>\n\u003C\u002Fol>\n",[],{"_modelApiKey":372,"copy":395,"listStyle":378,"darkTheme":11,"buttons":396,"color":370,"indent":11},"\u003Ch3>Getting started with Auth0\u003C\u002Fh3>\n\n\u003Cp>Auth0 is an identity platform to manage access to your applications. If you’re new to identity and access management (IAM), \u003Ca href=\"https:\u002F\u002Fauth0.com\u002Fdocs\u002Fget-started\u002Fidentity-fundamentals\u002Fidentity-and-access-management\">learn some of the basics\u003C\u002Fa> and plan a solution that best fits your technology requirements. If you’re familiar with IAM, then jump in and start building. You can start a new project by following this Auth0 \u003Ca href=\"https:\u002F\u002Fauth0.com\u002Fdocs\u002Fquickstart\u002Fwebapp\u002Fnextjs\">tutorial\u003C\u002Fa>, which uses the popular web framework \u003Ca href=\"https:\u002F\u002Fnextjs.org\u002F\">Next.js\u003C\u002Fa>.\u003C\u002Fp>\n\n\u003Cp>Today&#39;s proof of concept uses Next.js to show how to implement SSO with Auth0 and Commerce Layer. (By the way, there are many other frameworks you can use, but we’ve chosen Next.js.) We will show how to do this without using a lambda function or any other parts in the middle. It will also show how to use Commerce Layer&#39;s sales channel token and customer token to interact with Commerce Layer APIs.\u003C\u002Fp>\n",[],{"_modelApiKey":398,"title":399,"preTitle":93,"linkText":59,"darkTheme":11,"link":400},"link_block","API credentials",{"_modelApiKey":203,"text":59,"label":280,"subtitle":280,"icon":280,"isExternal":7,"url":401,"link":303,"anchor":280,"openForm":11,"form":303,"htmlClassNames":280},"https:\u002F\u002Fdocs.commercelayer.io\u002Fcore\u002Fapi-credentials",{"_modelApiKey":372,"copy":403,"listStyle":378,"darkTheme":11,"buttons":404,"color":370,"indent":11},"\u003Cp>Once you have properly configured your Auth0 account and set up a project that allows users to sign up, sign in, and logout, you can use Auth0 to synchronize customers with Commerce Layer. And lastly, by following the steps in the tutorial, you can enable users to log in and view their order history. Got it? Let’s dive in!\u003C\u002Fp>\n",[],{"id":406,"copy":407,"_modelApiKey":408,"listStyle":378,"subtitle":409,"title":410,"darkTheme":11,"buttons":411,"sectionId":412,"color":370},"TyI2hgmHS9aY3ZxdLk-Hng","\u003Cp>To interact with Commerce Layer APIs efficiently, add these Commerce Layer packages: \u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Ca href=\"github.com\u002Fcommercelayer\u002Fcommercelayer-js-auth\">JS Auth\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcommercelayer\u002Fcommercelayer-sdk\">JS SDK\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcommercelayer\u002Fcommercelayer-react-components\">React Components\u003C\u002Fa>\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Additionally, install \u003Ca href=\"https:\u002F\u002Funstorage.unjs.io\u002F\">unstorage\u003C\u002Fa> to support the storage management capabilities of \u003Ccode>js-auth\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Cp>We recommend using \u003Ca href=\"https:\u002F\u002Fpnpm.io\u002F\">pnpm\u003C\u002Fa> as the package manager for this example, but it should be straightforward to use \u003Ccode>npm\u003C\u002Fcode> or \u003Ccode>yarn\u003C\u002Fcode> instead:\u003C\u002Fp>\n","wysiwyg_section","Step 1","Add Commerce Layer dependencies",[],"step-1",{"_modelApiKey":414,"code":415,"showLineNumbers":11,"highlightLines":280,"language":416,"size":417},"code_block","pnpm add @commercelayer\u002Fjs-auth @commercelayer\u002Fsdk @commercelayer\u002Freact-components unstorage","shell","Wide",{"_modelApiKey":372,"copy":419,"listStyle":378,"darkTheme":11,"buttons":420,"color":370,"indent":11},"\u003Cp>To connect your project to Commerce Layer, you need to use \u003Ca href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fcore\u002Fapi-credentials#integration\">integration API credentials\u003C\u002Fa>. This kind of API credentials, with admin role, allows you to perform any CRUD action on any resource.\u003C\u002Fp>\n\n\u003Cp>To set it up, add the following environment variable to your \u003Ccode>.env.local\u003C\u002Fcode> file:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":422,"showLineNumbers":11,"highlightLines":280,"language":416,"size":423},"CL_INTEGRATION_CLIENT_ID=\u003Cyour-integration-client-id>\nCL_INTEGRATION_SECRET=\u003Cyour-integration-secret>","Standard",{"_modelApiKey":372,"copy":425,"listStyle":378,"darkTheme":11,"buttons":426,"color":370,"indent":11},"\u003Cp>Next, you need to use the \u003Ccode>js-auth\u003C\u002Fcode> package to obtain an integration token. We can use the \u003Ccode>makeIntegration\u003C\u002Fcode> helper that handles token requests, caching, expiration, and automatic refresh within the provided storage (memory in this example). The following code snippet demonstrates this implementation in the \u003Ccode>\u002Futils\u002FgetIntegration.js\u003C\u002Fcode> file:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":428,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"import { jwtDecode, makeIntegration } from '@commercelayer\u002Fjs-auth'\nimport { createStorage } from 'unstorage'\nimport memoryDriver from 'unstorage\u002Fdrivers\u002Fmemory'\nimport { CommerceLayer } from '@commercelayer\u002Fsdk'\n\nconst memoryStorage = createStorage({\n  driver: memoryDriver(),\n})\n\nconst integration = makeIntegration(\n  {\n    clientId: process.env.CL_INTEGRATION_CLIENT_ID,\n    clientSecret: process.env.CL_INTEGRATION_SECRET,\n    debug: true,\n  },\n  {\n    storage: memoryStorage,\n  },\n)\n\nexport default async function getClient() {\n  const token = await integration.getAuthorization()\n  const { payload } = jwtDecode(token.accessToken)\n  const client = CommerceLayer({\n    accessToken: token.accessToken,\n    organization: payload.organization.slug,\n  })\n  return client\n}","javascript",{"_modelApiKey":372,"copy":431,"listStyle":378,"darkTheme":11,"buttons":432,"color":370,"indent":11},"\u003Cp>This token will always be used in a Next.js API endpoint and will never be exposed on the client side. The same level of attention should be applied to the Auth0 token that we will use to update user metadata.\u003C\u002Fp>\n",[],{"id":434,"copy":435,"_modelApiKey":408,"listStyle":378,"subtitle":436,"title":437,"darkTheme":11,"buttons":438,"sectionId":439,"color":370},"CmO2cbo7RoSim60-cEysZg","\u003Cp>You have two way to interact with the Auth0 APIs:\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>Using the access token of the logged-in user.\u003C\u002Fli>\n\u003Cli>Using a new Machine to Machine application.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>The first approach may be easier, but it requires requesting more permissions from the user during authentication, which can lead to security issues — like a user updating their own metadata. You can review Auth0&#39;s \u003Ca href=\"https:\u002F\u002Fauth0.com\u002Fdocs\u002Fsecure\u002Ftokens\u002Faccess-tokens\u002Fget-management-api-tokens-for-single-page-applications#limitations\">documentation\u003C\u002Fa> to learn more about the limitations.\u003C\u002Fp>\n\n\u003Cp>For our POC, we will follow the Machine to Machine approach and interact with \u003Ca href=\"https:\u002F\u002Fauth0.com\u002Fdocs\u002Fapi\u002Fmanagement\u002Fv2\">Auth0 Management APIs\u003C\u002Fa>. To do this, you need to create a new application on Auth0:\u003C\u002Fp>\n","Step 2","Create a Machine to Machine (M2M) application on Auth0",[],"step-2",{"_modelApiKey":381,"border":7,"caption":441,"image":442,"size":390,"darkTheme":11,"rounded":7,"color":370},"Auth0 Machine to Machine application creation",{"url":443,"alt":444,"title":444,"width":289,"height":445,"format":290,"focalPoint":446,"customData":447},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1759770612-auth0-create-an-app.jpg","Auth0 create an app",1148,{"x":292,"y":292},{},{"_modelApiKey":372,"copy":449,"listStyle":378,"darkTheme":11,"buttons":450,"color":370,"indent":11},"\u003Cp>When using a free plan, be mindful of the limited number of tokens that can be requested per month (1000). These tokens expire after one day, so it is advisable to store them in memory (for example, using \u003Ca href=\"https:\u002F\u002Fredis.io\u002F\">Redis\u003C\u002Fa>) and reuse them across all the APIs that require access to the Auth0 Management API.\u003C\u002Fp>\n\n\u003Cp>After creating the application, you must set the following environment variables inside the \u003Ccode>.env.local\u003C\u002Fcode> file:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":452,"showLineNumbers":11,"highlightLines":280,"language":416,"size":417},"AUTH0_M2M_CLIENT_ID=\u003Cyour-machine-to-machine-client-id>\nAUTH0_M2M_CLIENT_SECRET=\u003Cyour-machine-to-machine-client-secret>",{"_modelApiKey":372,"copy":454,"listStyle":378,"darkTheme":11,"buttons":455,"color":370,"indent":11},"\u003Cp>When a user interacts with Auth0, a callback URL is invoked and a payload is received that can be used to add logic for interacting with the Commerce Layer API. Double check that a callback URL (\u003Ca href=\"http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fauth\u002Fcallback\">http:\u002F\u002Flocalhost:3000\u002Fauth\u002Fcallback\u003C\u002Fa> for our Next.js project) and a logout URL (\u003Ca href=\"http:\u002F\u002Flocalhost:3000\u002F\">http:\u002F\u002Flocalhost:3000\u002F\u003C\u002Fa>) have been setup within the Auth0 application that was created at the beginning:\u003C\u002Fp>\n",[],{"_modelApiKey":381,"border":7,"caption":457,"image":458,"size":390,"darkTheme":11,"rounded":7,"color":370},"Confirm the callback and logout URLs exist",{"url":459,"alt":460,"title":460,"width":289,"height":228,"format":290,"focalPoint":461,"customData":462},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1759770790-auth0-application-uris.jpg","Auth0 application URIs",{"x":292,"y":292},{},{"_modelApiKey":372,"copy":464,"listStyle":378,"darkTheme":11,"buttons":465,"color":370,"indent":11},"\u003Cp>And change the value of the environment variable \u003Ccode>AUTH0_SCOPE\u003C\u002Fcode> on \u003Ccode>.env.local\u003C\u002Fcode> file:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":467,"showLineNumbers":11,"highlightLines":280,"language":416,"size":423},"AUTH0_SCOPE='openid profile email'",{"id":469,"copy":470,"_modelApiKey":408,"listStyle":378,"subtitle":471,"title":472,"darkTheme":11,"buttons":473,"sectionId":474,"color":370},"OZGFHUACTKuGGmQQMMPD0w","\u003Cp>To ensure that a user is authenticated on both Commerce Layer and Auth0, we need to fetch or create the user on Commerce Layer and update their metadata on Auth0 with the customer ID provided by Commerce Layer. This can be achieved by receiving a callback when a user signs up or logs in. To do this, we need to add the following code inside \u003Ccode>\u002Flib\u002Fauth0.js\u003C\u002Fcode>. This code will enable us to receive the callback and update the necessary information accordingly:\u003C\u002Fp>\n","Step 3","Sync a user that authenticates to Commerce Layer",[],"step-3",{"_modelApiKey":414,"code":476,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"import { Auth0Client } from '@auth0\u002Fnextjs-auth0\u002Fserver'\nimport getOrCreateCustomer from '..\u002Futils\u002FgetOrCreateCustomer'\nimport { NextResponse } from 'next\u002Fserver'\n\n\u002F\u002F Initialize the Auth0 client\nexport const auth0 = new Auth0Client({\n  \u002F\u002F Options are loaded from environment variables by default\n  \u002F\u002F Ensure necessary environment variables are properly set\n  \u002F\u002F domain: process.env.AUTH0_DOMAIN,\n  \u002F\u002F clientId: process.env.AUTH0_CLIENT_ID,\n  \u002F\u002F clientSecret: process.env.AUTH0_CLIENT_SECRET,\n  \u002F\u002F appBaseUrl: process.env.APP_BASE_URL,\n  \u002F\u002F secret: process.env.AUTH0_SECRET,\n  authorizationParameters: {\n    \u002F\u002F In v4, the AUTH0_SCOPE and AUTH0_AUDIENCE environment variables are no longer automatically picked up by the SDK.\n    \u002F\u002F Instead, we need to provide the values explicitly.\n    scope: process.env.AUTH0_SCOPE,\n    audience: process.env.AUTH0_AUDIENCE,\n  },\n  \u002F\u002F onCallback hook replaces the afterCallback logic from v3\n  async onCallback(error, context, session) {\n    if (error) {\n      console.error('Authentication error:', error)\n      return NextResponse.redirect(new URL('\u002Ferror', process.env.APP_BASE_URL))\n    }\n\n    \u002F\u002F Custom logic after successful authentication - preserving your original afterCallback\n    if (session?.user) {\n      try {\n        await getOrCreateCustomer(session.user)\n      } catch (customerError) {\n        console.error('Error creating\u002Fgetting customer:', customerError)\n        \u002F\u002F Continue with authentication even if customer creation fails\n      }\n    }\n\n    \u002F\u002F Redirect to the return URL or default to home\n    return NextResponse.redirect(\n      new URL(context.returnTo || '\u002F', process.env.APP_BASE_URL),\n    )\n  },\n})\n",{"_modelApiKey":372,"copy":478,"listStyle":378,"darkTheme":11,"buttons":479,"color":370,"indent":11},"\u003Cp>We will now add the logic to update the metadata of an Auth0 user in the file \u003Ccode>\u002Futils\u002FgetOrCreateCustomer.js\u003C\u002Fcode>:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":481,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"import { ManagementClient } from 'auth0'\nimport getClient from '.\u002FgetIntegration'\n\nexport default async function getOrCreateCustomer(user) {\n  const auth0UserId = user.sub\n  const client = await getClient()\n  const customer = await client.customers.list({\n    filters: { email_eq: user.email },\n  })\n  let customerId\n\n  if (customer.length > 0) {\n    console.log('User already exists, syncing ID on Auth0')\n    customerId = customer[0].id\n  } else {\n    console.log(`Creating customer ${user.name}`)\n    const newCustomer = await client.customers.create({\n      email: user.email,\n      password: Math.random().toString(36).slice(-8),\n    })\n    customerId = newCustomer.id\n  }\n\n  const currentUserManagementClient = new ManagementClient({\n    domain: process.env.AUTH0_DOMAIN,\n    clientId: process.env.AUTH0_M2M_CLIENT_ID,\n    clientSecret: process.env.AUTH0_M2M_CLIENT_SECRET,\n    scope: 'read:users update:users',\n  })\n\n  try {\n    \u002F\u002F Auth0 Management API v5 syntax\n    const result = await currentUserManagementClient.users.update(auth0UserId, {\n      user_metadata: { customerId },\n    })\n\n    console.log('User metadata updated successfully:', result?.user_metadata)\n  } catch (error) {\n    console.error('Error on updating the user metadata:', error)\n    throw error\n  }\n}",{"_modelApiKey":372,"copy":483,"listStyle":378,"darkTheme":11,"buttons":484,"color":370,"indent":11},"\u003Cp>In the above code, we obtain a Commerce Layer client using an integration token. We then attempt to locate the customer by email on Commerce Layer. If the customer is not present, we create a new one. We retrieve the customer ID, obtain an Auth0 client, and update the user&#39;s metadata with the \u003Ccode>customerId\u003C\u002Fcode> attribute. Before running the project we need to add the auth0 missing package that we will use to interact with Auth0 Management APIs:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":486,"showLineNumbers":11,"highlightLines":280,"language":416,"size":423},"pnpm add auth0",{"id":488,"copy":489,"_modelApiKey":408,"listStyle":378,"subtitle":490,"title":491,"darkTheme":11,"buttons":492,"sectionId":493,"color":370},"Fb2W7O37TQiWMOrS3RwYSA","\u003Cp>Next, we need to add a React component to handle the Commerce Layer \u003Ca href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fcore\u002Fapi-credentials#sales-channel\">sales channel tokens\u003C\u002Fa>. If the user is not authenticated with Auth0, the user will navigate in guest mode and the provider should return a sales channel token with \u003Ca href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fcore\u002Froles-and-permissions#sales-channel\">this set of permissions\u003C\u002Fa>. However, if the user is authenticated with Auth0, the provider should return a \u003Ca href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fcore\u002Froles-and-permissions#password\">customer token\u003C\u002Fa> owned by the signed-in user. From that point forward, the user will interact on behalf of that user.\u003C\u002Fp>\n\n\u003Cp>First of all we need to add the following credentials to get the sales channel token on \u003Ccode>.env.local\u003C\u002Fcode>:\u003C\u002Fp>\n","Step 4","Manage Commerce Layer tokens with a provider",[],"step-4",{"_modelApiKey":414,"code":495,"showLineNumbers":11,"highlightLines":280,"language":416,"size":417},"NEXT_PUBLIC_CL_SALES_CHANNEL_CLIENT_ID=\u003Cyour-sales-channel-client-id>\nNEXT_PUBLIC_CL_MARKET=\u003Cyour-market-number>",{"_modelApiKey":372,"copy":497,"listStyle":378,"darkTheme":11,"buttons":498,"color":370,"indent":11},"\u003Cp>The provider will store the token on the local storage and check if it is still valid, reusing it until it expires. To check if the user is logged in or not, we will use the \u003Ccode>useUser\u003C\u002Fcode> hook from the \u003Ccode>@auth0\u002Fnextjs-auth0\u003C\u002Fcode> package. If the user is logged in, we will try to get the sales channel token, otherwise, we will request it from the \u003Ccode>\u002Fapi\u002Ftoken\u003C\u002Fcode> endpoint (which we will discuss later) and store the token on the storage, using the new \u003Ccode>js-auth\u003C\u002Fcode>  library. On the other hand, if the user is not logged in, we will remove the customer token from the local storage (if it&#39;s present), and get a sales channel token.\u003C\u002Fp>\n\n\u003Cp>Here the file \u003Ccode>\u002Fproviders\u002FCommerceLayerAuth.jsx\u003C\u002Fcode>:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":500,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"import { createContext, useContext, useEffect, useState } from 'react'\nimport { makeSalesChannel } from '@commercelayer\u002Fjs-auth'\nimport { useUser } from '@auth0\u002Fnextjs-auth0'\nimport { createStorage } from 'unstorage'\nimport localStorageDriver from 'unstorage\u002Fdrivers\u002Flocalstorage'\n\nconst CommerceLayerAuthContext = createContext()\n\nexport const CommerceLayerAuthProvider = ({ children }) => {\n  const { user, isLoading } = useUser()\n  const [auth, setAuth] = useState(null)\n  const [isLoadingAuth, setIsLoadingAuth] = useState(true)\n  const [salesChannel, setSalesChannel] = useState(null)\n\n  useEffect(() => {\n    function fetchToken() {\n      const storage = createStorage({\n        driver: localStorageDriver({ base: 'auth0_sample:' }),\n      })\n\n      const salesChannel = makeSalesChannel(\n        {\n          clientId: process.env.NEXT_PUBLIC_CL_SALES_CHANNEL_CLIENT_ID,\n          scope: process.env.NEXT_PUBLIC_CL_MARKET,\n          debug: true,\n        },\n        {\n          storage,\n        },\n      )\n      setSalesChannel(salesChannel)\n    }\n\n    fetchToken()\n  }, [])\n\n  useEffect(() => {\n    async function fetchToken() {\n      if (!isLoading) {\n        const tokenData = await salesChannel.getAuthorization()\n\n        if (user) {\n          if (tokenData.ownerType === 'customer') {\n            console.log('Set customer token from local storage', tokenData)\n            setAuth(tokenData)\n          } else {\n            console.log('Get customer token from \u002Fapi\u002Ftoken')\n\n            const response = await fetch('api\u002Ftoken').then((response) =>\n              response.json(),\n            )\n            if (!response.token.errors?.length) {\n              try {\n                salesChannel.setCustomer({\n                  accessToken: response.token.accessToken,\n                  scope: tokenData.scope,\n                })\n                setAuth({\n                  accessToken: response.token.accessToken,\n                  expires: new Date(response.token.expires),\n                })\n              } catch (error) {\n                console.error('Error setting customer:', error)\n              }\n            }\n          }\n        } else {\n          if (tokenData) {\n            console.log('Get sales channel token from local storage', tokenData)\n            setAuth({\n              accessToken: tokenData.accessToken,\n              expires: tokenData.expires,\n            })\n          }\n        }\n        setIsLoadingAuth(false)\n      } else {\n        setIsLoadingAuth(true)\n      }\n    }\n    fetchToken()\n  }, [user, isLoading])\n\n  const logout = async () => {\n    await salesChannel.logoutCustomer()\n    window.location.href = '\u002Fauth\u002Flogout'\n  }\n\n  return (\n    \u003CCommerceLayerAuthContext.Provider\n      value={{ auth, logout, isLoading: isLoadingAuth }}\n    >\n      {children}\n    \u003C\u002FCommerceLayerAuthContext.Provider>\n  )\n}\n\nexport const useCommerceLayerAuth = () => useContext(CommerceLayerAuthContext)",{"_modelApiKey":372,"copy":502,"listStyle":378,"darkTheme":11,"buttons":503,"color":370,"indent":11},"\u003Cp>The provider efficiently manages tokens with built-in caching and validation. This eliminates the need to request or create new tokens when they&#39;re still valid during page refreshes or when users return to the site. When a user logs out, we delete the customer token using the \u003Ccode>logout\u003C\u002Fcode> function and revert to the previous sales channel token, assuming it remains valid. Now you need to use the provider in the \u003Ccode>\u002Fapp\u002F_layout.jsx\u003C\u002Fcode> file:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":505,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"'use client'\n\nimport '.\u002Fglobals.css'\nimport NavBar from '..\u002Fcomponents\u002FNavBar'\nimport { Container } from 'reactstrap'\nimport Footer from '..\u002Fcomponents\u002FFooter'\nimport React from 'react'\nimport { Auth0Provider } from '@auth0\u002Fnextjs-auth0'\nimport { CommerceLayerAuthProvider } from '..\u002Fproviders\u002FCommerceLayerAuth'\nimport initFontAwesome from '..\u002Futils\u002FinitFontAwesome'\n\n\u002F\u002F Initialize FontAwesome icons\ninitFontAwesome()\n\nexport default function RootLayout({ children }) {\n  return (\n    \u003Chtml lang=\"en\">\n      \u003Chead>\n        \u003Clink\n          rel=\"stylesheet\"\n          href=\"https:\u002F\u002Fstackpath.bootstrapcdn.com\u002Fbootstrap\u002F4.3.1\u002Fcss\u002Fbootstrap.min.css\"\n          integrity=\"sha384-ggOyR0iXCbMQv3Xipma34MD+dH\u002F1fQ784\u002Fj6cY\u002FiJTQUOhcWr7x9JvoRxT2MZw1T\"\n          crossOrigin=\"anonymous\"\n        \u002F>\n        \u003Clink\n          rel=\"stylesheet\"\n          href=\"https:\u002F\u002Fcdn.auth0.com\u002Fjs\u002Fauth0-samples-theme\u002F1.0\u002Fcss\u002Fauth0-theme.min.css\"\n        \u002F>\n      \u003C\u002Fhead>\n      \u003Cbody>\n        \u003CAuth0Provider>\n          \u003CCommerceLayerAuthProvider>\n            \u003Cmain\n              id=\"app\"\n              className=\"d-flex flex-column h-100\"\n              data-testid=\"layout\"\n            >\n              \u003CNavBar \u002F>\n              \u003CContainer className=\"flex-grow-1 mt-5\">{children}\u003C\u002FContainer>\n              \u003CFooter \u002F>\n            \u003C\u002Fmain>\n          \u003C\u002FCommerceLayerAuthProvider>\n        \u003C\u002FAuth0Provider>\n      \u003C\u002Fbody>\n    \u003C\u002Fhtml>\n  )\n}",{"id":507,"copy":508,"_modelApiKey":408,"listStyle":378,"subtitle":509,"title":510,"darkTheme":11,"buttons":511,"sectionId":512,"color":370},"PW9WS9PAQbmiYj5P45DBOQ","\u003Cp>To obtain a customer token, we need to use the \u003Ca href=\"https:\u002F\u002Fdocs.commercelayer.io\u002Fcore\u002Fauthentication\u002Fjwt-bearer\">JWT bearer\u003C\u002Fa> flow on the \u003Ccode>\u002Fapi\u002Ftoken\u003C\u002Fcode> API endpoint. The token will be used to make API calls to Commerce Layer as a customer. The JWT bearer flow is only available to Enterprise customers.\u003C\u002Fp>\n\n\u003Cp>To use this flow we have to create an assertion containing the customer ID.\u003C\u002Fp>\n","Step 5","Request the customer token",[],"step-5",{"_modelApiKey":414,"code":514,"showLineNumbers":11,"highlightLines":280,"language":416,"size":423},"pnpm run dev",{"_modelApiKey":372,"copy":516,"listStyle":378,"darkTheme":11,"buttons":517,"color":370,"indent":11},"\u003Cp>To obtain the customer token, we need to create a protected Next.js endpoint (\u003Ccode>\u002Fapp\u002Fapi\u002Ftoken\u002Froute.js\u003C\u002Fcode>). This endpoint uses the \u003Ccode>getSession\u003C\u002Fcode> function from the \u003Ccode>\u002Flib\u002Fauth0.js\u003C\u002Fcode> file to ensure that the user is authenticated with Auth0.\u003C\u002Fp>\n\n\u003Cp>First, we retrieve the user from the session to obtain the user ID on Auth0. Then, we fetch the user from Auth0 to obtain the Commerce Layer customer ID from the user&#39;s metadata. We then get the customer token using the JWT bearer flow.\u003C\u002Fp>\n\n\u003Cp>We also added two headers on the request to the Auth API endpoint to avoid being rate limited making a server side request for each customer logging in. Those headers are:\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Ccode>x-backend-auth\u003C\u002Fcode> — this key is mandatory for using the \u003Ccode>x-true-client-ip\u003C\u002Fcode> header to forward the client IP address, which helps in managing rate limits effectively by identifying unique client requests. \u003C\u002Fli>\n\u003Cli>\u003Ccode>x-true-client-ip\u003C\u002Fcode> — allows to forward the client IP address in server-side requests. Rate limits will be based on individual client IP addresses rather than the server&#39;s IP address alone.\u003C\u002Fli>\n\u003C\u002Ful>\n",[],{"_modelApiKey":367,"icon":368,"copy":519,"color":370},"\u003Cp>\u003Ccode>x-backend-auth\u003C\u002Fcode> key is available only to enterprise customers.\u003C\u002Fp>\n",{"_modelApiKey":414,"code":521,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"import { auth0 } from '..\u002F..\u002F..\u002Flib\u002Fauth0'\nimport { ManagementClient } from 'auth0'\nimport { authenticate, createAssertion } from '@commercelayer\u002Fjs-auth'\nimport { NextResponse } from 'next\u002Fserver'\n\nexport const GET = async function token(request) {\n  try {\n    \u002F\u002F In v4, getSession() needs the request object in API routes\n    const session = await auth0.getSession()\n\n    if (!session) {\n      return NextResponse.json({ error: 'Not authenticated' }, { status: 401 })\n    }\n\n    const managementClient = new ManagementClient({\n      domain: process.env.AUTH0_DOMAIN,\n      clientId: process.env.AUTH0_M2M_CLIENT_ID,\n      clientSecret: process.env.AUTH0_M2M_CLIENT_SECRET,\n    })\n\n    \u002F\u002F Ensure user.sub is a valid string\n    if (!session.user.sub || typeof session.user.sub !== 'string') {\n      console.error('Invalid user.sub:', session.user.sub)\n      return res.status(400).json({ error: 'Invalid user ID' })\n    }\n\n    const user = await managementClient.users.get(session.user.sub)\n\n    const token = await authenticate(\n      'urn:ietf:params:oauth:grant-type:jwt-bearer',\n      {\n        clientId: process.env.CL_SALES_CHANNEL_CLIENT_ID,\n        clientSecret: process.env.CL_SALES_CHANNEL_SECRET,\n        scope: process.env.NEXT_PUBLIC_CL_MARKET,\n        assertion: await createAssertion({\n          payload: {\n            'https:\u002F\u002Fcommercelayer.io\u002Fclaims': {\n              owner: {\n                type: 'Customer',\n                id: user.user_metadata.customerId,\n              },\n            },\n          },\n        }),\n        headers: {\n          'x-backend-auth': process.env.CL_BACKEND_AUTH_KEY,\n          'x-true-client-ip': request.socket?.remoteAddress,\n        },\n      },\n    )\n    return NextResponse.json(\n      {\n        token: token,\n      },\n      { status: 200 },\n    )\n  } catch (error) {\n    console.log(error)\n    return NextResponse.json(\n      { error: error.message },\n      { status: error.status || 500 },\n    )\n  }\n}",{"_modelApiKey":372,"copy":523,"listStyle":378,"darkTheme":11,"buttons":524,"color":370,"indent":11},"\u003Cp>Now you can utilize the \u003Ccode>useCommerceLayerAuth\u003C\u002Fcode> hook to access tokens. This hook automatically provides the appropriate token based on the user&#39;s authentication status. When a user is logged in, you can use this token to interact with Commerce Layer APIs and display their order history.\u003C\u002Fp>\n",[],{"id":526,"copy":527,"_modelApiKey":408,"listStyle":378,"subtitle":528,"title":529,"darkTheme":11,"buttons":530,"sectionId":531,"color":370},"V67fIr2BRLuowyEpRKGo1A","\u003Cp>To proceed, create a new page at \u003Ccode>\u002Fpages\u002Forders.jsx\u003C\u002Fcode>. This page will only be visible to logged-in users:\u003C\u002Fp>\n","Step 6","Build the order history page",[],"step-6",{"_modelApiKey":414,"code":533,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"'use client'\n\nimport { useUser } from '@auth0\u002Fnextjs-auth0'\nimport {\n  CommerceLayer,\n  CustomerContainer,\n  OrderListEmpty,\n  OrderList,\n  OrderListRow,\n} from '@commercelayer\u002Freact-components'\nimport { useCommerceLayerAuth } from '..\u002F..\u002Fproviders\u002FCommerceLayerAuth'\n\nconst columns = [\n  {\n    header: 'Order',\n    accessorKey: 'number',\n  },\n  {\n    header: 'Status',\n    accessorKey: 'status',\n  },\n  {\n    header: 'Date',\n    accessorKey: 'updated_at',\n  },\n  {\n    header: 'Amount',\n    accessorKey: 'formatted_total_amount_with_taxes',\n    id: 'total_amount_cents',\n  },\n]\n\nexport default function Orders() {\n  const { user, isLoading, error } = useUser()\n  const { auth, isLoading: isLoadingCL } = useCommerceLayerAuth()\n\n  if (error) {\n    return \u003Cdiv>{error.message}\u003C\u002Fdiv>\n  }\n\n  if (isLoading) {\n    return \u003Cdiv>Loading...\u003C\u002Fdiv>\n  }\n\n  return (\n    \u003C>\n      {!isLoadingCL && (\n        \u003Cdiv className=\"mb-5\">\n          \u003Ch1>Orders\u003C\u002Fh1>\n          \u003Cdiv>\n            \u003CCommerceLayer accessToken={auth.accessToken}>\n              \u003CCustomerContainer>\n                \u003COrderList\n                  type=\"orders\"\n                  className=\"table table-bordered w-100\"\n                  columns={columns}\n                  theadClassName=\"thead-light\"\n                  rowTrClassName=\"border-bottom\"\n                  showPagination\n                  pageSize={15}\n                  paginationContainerClassName=\"d-flex justify-content-between align-items-center\"\n                >\n                  \u003COrderListEmpty \u002F>\n\n                  \u003COrderListRow field=\"number\">\n                    {({ cell, order, ...p }) => {\n                      return (\n                        \u003C>\n                          {cell?.map((cell, index) => (\n                            \u003Cdiv {...p} key={cell.id}>\n                              \u003Cp\n                                className=\"font-weight-bold\"\n                                key={`order-${cell.id}-${index}`}\n                              >\n                                Order # {cell.renderValue()}\n                              \u003C\u002Fp>\n                            \u003C\u002Fdiv>\n                          ))}\n                        \u003C\u002F>\n                      )\n                    }}\n                  \u003C\u002FOrderListRow>\n                  \u003COrderListRow\n                    field=\"status\"\n                    className=\"align-top border-bottom\"\n                  \u002F>\n                  \u003COrderListRow\n                    field=\"updated_at\"\n                    className=\"align-top border-bottom\"\n                  \u002F>\n                  \u003COrderListRow\n                    field=\"total_amount_cents\"\n                    className=\"align-top border-bottom font-weight-bold\"\n                  \u002F>\n                \u003C\u002FOrderList>\n              \u003C\u002FCustomerContainer>\n            \u003C\u002FCommerceLayer>\n          \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n      )}\n    \u003C\u002F>\n  )\n}",{"_modelApiKey":372,"copy":535,"listStyle":378,"darkTheme":11,"buttons":536,"color":370,"indent":11},"\u003Cp>You can now change the link to the external page on the \u003Ccode>\u002Fcomponents\u002FNavBar.jsx\u003C\u002Fcode> component to the orders page from this:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":538,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"\u003CNavItem>\n  \u003CPageLink href=\"\u002Fexternal\" className=\"nav-link\" testId=\"navbar-external\">\n    External API\n\t\u003C\u002FPageLink>\n\u003C\u002FNavItem>",{"_modelApiKey":372,"copy":540,"listStyle":378,"darkTheme":11,"buttons":541,"color":370,"indent":11},"\u003Cp>To this:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":543,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"\u003CNavItem>\n  \u003CPageLink href=\"\u002Forders\" className=\"nav-link\">\n    Orders\n  \u003C\u002FPageLink>\n\u003C\u002FNavItem>",{"_modelApiKey":372,"copy":545,"listStyle":378,"darkTheme":11,"buttons":546,"color":370,"indent":11},"\u003Cp>And change the logout button to use the \u003Ccode>logout\u003C\u002Fcode> function of the \u003Ccode>CommerceLayerAuth\u003C\u002Fcode> provider. The function will leverage the new \u003Ccode>logoutCustomer\u003C\u002Fcode> function of the \u003Ccode>js-auth\u003C\u002Fcode>:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":548,"showLineNumbers":11,"highlightLines":280,"language":429,"size":423},"...\nconst { logout } = useCommerceLayerAuth()\n...\n\u003CAnchorLink\n  onClick={logout}\n  icon=\"power-off\"\n  testId=\"navbar-logout-desktop\"\n>\n  Log out\n\u003C\u002FAnchorLink>\n...",{"_modelApiKey":372,"copy":550,"listStyle":378,"darkTheme":11,"buttons":551,"color":370,"indent":11},"\u003Cp>You can also change the \u003Ccode>\u002Fcomponents\u002FAnchorLink.jsx\u003C\u002Fcode> component to handle the \u003Ccode>onClick\u003C\u002Fcode> prop:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":553,"showLineNumbers":11,"highlightLines":280,"language":429,"size":423},"import React from 'react'\n\nimport NavBarItem from '.\u002FNavBarItem'\n\nconst AnchorLink = ({\n  children,\n  href,\n  onClick,\n  className,\n  icon,\n  tabIndex,\n  testId,\n}) => {\n  return (\n    \u003Ca href={href} onClick={onClick}>\n      \u003CNavBarItem\n        href={href}\n        className={className}\n        icon={icon}\n        tabIndex={tabIndex}\n        testId={testId}\n      >\n        {children}\n      \u003C\u002FNavBarItem>\n    \u003C\u002Fa>\n  )\n}\n\nexport default AnchorLink",{"_modelApiKey":372,"copy":555,"listStyle":378,"darkTheme":11,"buttons":556,"color":370,"indent":11},"\u003Cp>After logging in with your account, you should be able to access the orders page and view your orders.\u003C\u002Fp>\n",[],{"_modelApiKey":381,"border":7,"caption":280,"image":558,"size":390,"darkTheme":11,"rounded":7,"color":370},{"url":559,"alt":560,"title":560,"width":289,"height":561,"format":290,"focalPoint":562,"customData":563},"https:\u002F\u002Fwww.datocms-assets.com\u002F35053\u002F1759936518-auth0-orders.jpg","Auth0 orders",888,{"x":292,"y":292},{},{"id":565,"copy":566,"_modelApiKey":408,"listStyle":378,"subtitle":567,"title":568,"darkTheme":11,"buttons":569,"sectionId":570,"color":370},"HKedKnEZSaCMQV8tNypDCw","\u003Cp>If you don&#39;t have any orders for your new customer, you can add the following components to \u003Ccode>\u002Fcomponents\u002FContent.jsx\u003C\u002Fcode>. This will allow you to add a product to the cart, proceed to checkout, and see the newly placed order at \u003Ca href=\"http:\u002F\u002Flocalhost:3000\u002Forders\">http:\u002F\u002Flocalhost:3000\u002Forders\u003C\u002Fa> while still logged in.\u003C\u002Fp>\n","Step 7","Place an order and see it on your history page",[],"step-7",{"_modelApiKey":414,"code":572,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"import React from 'react'\nimport { Row, Col } from 'reactstrap'\nimport { useCommerceLayerAuth } from '..\u002Fproviders\u002FCommerceLayerAuth'\nimport {\n  CommerceLayer,\n  OrderContainer,\n  OrderStorage,\n  SkusContainer,\n  Skus,\n  SkuField,\n  PricesContainer,\n  Price,\n  AddToCartButton,\n} from '@commercelayer\u002Freact-components'\n\nconst skuCode = 'BABYONBU000000E63E7412MX'\nconst websiteUrl = 'http:\u002F\u002Flocalhost:3000'\n\nconst Content = () => {\n  const { auth } = useCommerceLayerAuth()\n\n  if (!auth) {\n    return null\n  }\n  return (\n    \u003CCommerceLayer accessToken={auth?.accessToken}>\n      \u003Cdiv className=\"next-steps my-5\">\n        \u003CRow className=\"d-flex justify-content-between\">\n          \u003CCol key={0} md={5} className=\"mb-4\">\n            \u003COrderStorage persistKey=\"my-order\">\n              \u003COrderContainer\n                attributes={{ return_url: websiteUrl, cart_url: websiteUrl }}\n              >\n                \u003CSkusContainer skus={[skuCode]}>\n                  \u003Cdiv className=\"card\" style={{ width: '18rem' }}>\n                    \u003CSkus>\n                      \u003CSkuField\n                        attribute=\"image_url\"\n                        className=\"card-img-top\"\n                        tagElement=\"img\"\n                      \u002F>\n                      \u003Cdiv className=\"card-body\">\n                        \u003CSkuField\n                          attribute=\"name\"\n                          className=\"card-title\"\n                          tagElement=\"h5\"\n                        \u002F>\n                        \u003CSkuField\n                          attribute=\"description\"\n                          className=\"card-text\"\n                          tagElement=\"p\"\n                        \u002F>\n                        \u003CPricesContainer>\n                          \u003CPrice skuCode={skuCode}>\n                            {({ prices }) => {\n                              if (prices.length > 0) {\n                                const {\n                                  formatted_amount,\n                                  formatted_compare_at_amount,\n                                } = prices[0]\n                                return (\n                                  \u003Cp>\n                                    \u003Cspan>{formatted_amount}\u003C\u002Fspan>{' '}\n                                    \u003Cspan>\n                                      \u003Cdel>{formatted_compare_at_amount}\u003C\u002Fdel>\n                                    \u003C\u002Fspan>\n                                  \u003C\u002Fp>\n                                )\n                              }\n                            }}\n                          \u003C\u002FPrice>\n                        \u003C\u002FPricesContainer>\n                        \u003CAddToCartButton\n                          className=\"btn btn-primary\"\n                          buyNowMode\n                          skuCode={skuCode}\n                        \u002F>\n                      \u003C\u002Fdiv>\n                    \u003C\u002FSkus>\n                  \u003C\u002Fdiv>\n                \u003C\u002FSkusContainer>\n              \u003C\u002FOrderContainer>\n            \u003C\u002FOrderStorage>\n          \u003C\u002FCol>\n        \u003C\u002FRow>\n      \u003C\u002Fdiv>\n    \u003C\u002FCommerceLayer>\n  )\n}\n\nexport default Content",{"_modelApiKey":372,"copy":574,"listStyle":378,"darkTheme":11,"buttons":575,"color":370,"indent":11},"\u003Cp>Please modify the file \u003Ccode>\u002Fpages\u002Findex.jsx\u003C\u002Fcode> as follows:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":577,"showLineNumbers":11,"highlightLines":280,"language":429,"size":417},"import React from 'react';\n\nimport Content from '..\u002Fcomponents\u002FContent';\n\nexport default function Index() {\n  return \u003CContent \u002F>;\n}",{"_modelApiKey":372,"copy":579,"listStyle":378,"darkTheme":11,"buttons":580,"color":370,"indent":11},"\u003Cp>On the homepage, you should see the product with the code \u003Ccode>TSHIRTMS000000FFFFFFLXXX\u003C\u002Fcode> (If you have not used our seeder to feed data on your organization, the SKU code may differ and I suggest you to use one of your SKUs). Add it to your cart, check out, and view the order in your user area.\u003C\u002Fp>\n",[],{"_modelApiKey":372,"copy":582,"listStyle":378,"darkTheme":11,"buttons":583,"color":370,"indent":11},"\u003Ch3>Bonus point\u003C\u002Fh3>\n\n\u003Cp>To test private markets and logged-in customers, you can create a new market in Commerce Layer with a different price list. Define a price for the same SKU (\u003Ccode>TSHIRTMS000000FFFFFFLXXX\u003C\u002Fcode>) on this page. Next, create a customer group and assign it to both the market and your customer in Commerce Layer. Then add the new market to your \u003Ccode>.env.local\u003C\u002Fcode> file:\u003C\u002Fp>\n",[],{"_modelApiKey":414,"code":585,"showLineNumbers":11,"highlightLines":280,"language":416,"size":423},"CL_PRIVATE_MARKET=market:code:B2B",{"_modelApiKey":372,"copy":587,"listStyle":378,"darkTheme":11,"buttons":588,"color":370,"indent":11},"\u003Cp>Finally, modify the \u003Ccode>\u002Fapi\u002Ftoken\u002Froutes.js\u003C\u002Fcode> file at line 34 by changing \u003Ccode>scope: process.env.NEXT_PUBLIC_CL_MARKET,\u003C\u002Fcode> to \u003Ccode>scope: process.env.CL_PRIVATE_MARKET,\u003C\u002Fcode>\u003C\u002Fp>\n\n\u003Cp>When you visit the home page while logged in, you should see a different price reflecting your customer group&#39;s pricing.\u003C\u002Fp>\n\n\u003Ch3>Conclusion\u003C\u002Fh3>\n\n\u003Cp>We hope you have learned more about ways to use our SSO feature, which is available to enterprise customers. You may also want to consider other identity provider platforms, such as \u003Ca href=\"https:\u002F\u002Fwww.okta.com\u002F\">Okta\u003C\u002Fa> or \u003Ca href=\"https:\u002F\u002Fwww.ory.sh\u002F\">Ory\u003C\u002Fa>, that offer similar features and a seamless integration with Commerce Layer.\u003C\u002Fp>\n\n\u003Cp>Stay tuned for more articles in this series!\u003C\u002Fp>\n",[],{"_modelApiKey":367,"icon":590,"copy":591,"color":370},"code-simple","\u003Cp>You can explore \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcommercelayer\u002Fexamples\u002Ftree\u002Fmain\u002Fauthentication\u002Fnextjs-auth0-sso\">this GitHub repository\u003C\u002Fa> , which contains all the code used in this tutorial, for further learning and code customization. Thank you for reading this far. Cheers!\u003C\u002Fp>\n",[593,594,596,598,600,602,604,606,608,610,612,614,616,618,620,622,624],{"attributes":303,"tag":304,"content":305},{"attributes":595,"tag":309,"content":303},{"property":308,"content":305},{"attributes":597,"tag":309,"content":303},{"name":312,"content":305},{"attributes":599,"tag":309,"content":303},{"name":315,"content":316},{"attributes":601,"tag":309,"content":303},{"property":319,"content":316},{"attributes":603,"tag":309,"content":303},{"name":322,"content":316},{"attributes":605,"tag":309,"content":303},{"property":325,"content":326},{"attributes":607,"tag":309,"content":303},{"property":329,"content":330},{"attributes":609,"tag":309,"content":303},{"property":333,"content":334},{"attributes":611,"tag":309,"content":303},{"name":337,"content":326},{"attributes":613,"tag":309,"content":303},{"property":340,"content":341},{"attributes":615,"tag":309,"content":303},{"property":344,"content":345},{"attributes":617,"tag":309,"content":303},{"property":348,"content":349},{"attributes":619,"tag":309,"content":303},{"property":352,"content":353},{"attributes":621,"tag":309,"content":303},{"property":356,"content":280},{"attributes":623,"tag":309,"content":303},{"name":359,"content":360},{"attributes":625,"tag":309,"content":303},{"name":363,"content":364},[627],{"name":285,"picture":628},{"url":287,"alt":288,"title":288,"width":289,"height":289,"format":290,"focalPoint":629,"customData":630},{"x":292,"y":292},{},1790593723507]