<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: FusionAuth</title>
    <description>The latest articles on Forem by FusionAuth (@fusionauth).</description>
    <link>https://forem.com/fusionauth</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F139081%2Fb4ef859c-da27-4126-934a-fda3a64f70e9.png</url>
      <title>Forem: FusionAuth</title>
      <link>https://forem.com/fusionauth</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/fusionauth"/>
    <language>en</language>
    <item>
      <title>I Just Want Authentication To Work</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Wed, 22 Nov 2023 23:32:35 +0000</pubDate>
      <link>https://forem.com/fusionauthio/i-just-want-authentication-to-work-1idb</link>
      <guid>https://forem.com/fusionauthio/i-just-want-authentication-to-work-1idb</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted by Alex Patterson @ FusionAuth.io&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Authentication is the process of verifying the identity of a user or entity. It is a fundamental security measure that is used in a wide variety of applications, including web applications, mobile apps, and APIs. The thing is, as a developer with over 20 years of experience, I just don't care about figuring it out! I just want it to work!!&lt;/p&gt;

&lt;p&gt;There are many different ways to implement authentication, but the most common approach is to use a username and password. Yep, just like back in 1960 when &lt;a href="https://youtu.be/KtY3L5tDN_E?si=BATB3BzJJFpWsHmB" rel="noopener noreferrer"&gt;The Everly Brothers&lt;/a&gt; had one of the top songs out there, this method is still most popular today. Just as the Everly Brothers' time at the top of the charts has passed, so has the time for passwords. I don't want to hear about them anymore!&lt;/p&gt;

&lt;p&gt;A more modern approach is to use a single sign-on (SSO) solution. SSO solutions allow users to log in to multiple applications using a single set of credentials. This can be convenient for users, as they do not have to remember multiple usernames and passwords. This method came about around the same time Eminem hit the &lt;a href="https://youtu.be/6mPCy9LMxTI?si=5A2I-N1neb_8rGOl" rel="noopener noreferrer"&gt;billboard top 100&lt;/a&gt; for the first time. SSO often has the problem of including the first login requirement of using a username and password to this day. Even Eminem made some changes and remains relevant!&lt;/p&gt;

&lt;p&gt;My point is that authentication is an important security measure, but it can also be complex to implement. This is why we have been hanging on to these old methods for so long! When you are creating a new application (unless it is an &lt;em&gt;authentication&lt;/em&gt; application), the last thing you care about is user login. There are many different authentication methods to choose from, and each method has its own advantages and disadvantages. Additionally, authentication needs to be implemented in a secure way to prevent attackers from gaining unauthorized access to applications. &lt;br&gt;
But, again, all I care about is that my data is secure when creating the next &lt;a href="https://youtu.be/CKrdsGdLVQ8?si=bTp4DHKS4tlpQzfW" rel="noopener noreferrer"&gt;Napster&lt;/a&gt; so everyone can share their 1990s music with me again!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fimg%2Fblogs%2Fi-just-want-authentication-to-work%2Fnapster.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fimg%2Fblogs%2Fi-just-want-authentication-to-work%2Fnapster.png" alt="Napster app from Wikipedia"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The benefits of using an authentication service
&lt;/h2&gt;

&lt;p&gt;There are many benefits to using an authentication service. Authentication services can help you:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save time and money: Implementing and maintaining authentication can be time-consuming and expensive. authentication services can save you the hassle of having to implement and maintain your authentication system. s&lt;/li&gt;
&lt;li&gt;Improve security: authentication services can help you to improve the security of your applications by using best practices and the latest security technologies keeping up to date with the latest security patches.&lt;/li&gt;
&lt;li&gt;Reduce the risk of compliance violations: authentication services can help you meet compliance requirements by providing features such as two-factor authentication and user provisioning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last thing that I want is a call on Saturday night that someone hacked the password database that we built in-house two years ago and didn't maintain securely!  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fimg%2Fblogs%2Fi-just-want-authentication-to-work%2Fmeme-leak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fimg%2Fblogs%2Fi-just-want-authentication-to-work%2Fmeme-leak.png" alt="Meme you can't leak passwords if you don't store passwords, man pointing at head"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing an authentication service
&lt;/h2&gt;

&lt;p&gt;When choosing an authentication service, there are a few factors to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Features: Consider the features that are important to you, such as two-factor authentication, user provisioning, and support for different authentication methods.&lt;/li&gt;
&lt;li&gt;Pricing: authentication services can vary in price. Compare the pricing of different services to find the best deal for your needs.&lt;/li&gt;
&lt;li&gt;Ease of use: Consider how easy the authentication service is to use. You want a service that is easy to set up and manage.&lt;/li&gt;
&lt;li&gt;Hosting: Do you need flexibility to host your instance of an authentication Provider or have them host?&lt;/li&gt;
&lt;li&gt;Integration support: Often you'll want to use an authentication service as a central user data store. You'll want to make sure you can integrate custom applications, off-the-shelf commercial software, and open-source solutions with it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  For Developers Who Just Want It To Work
&lt;/h2&gt;

&lt;p&gt;If you are a developer who just wants authentication to work without implementing it yourself, there are a few things that you can do to make your life easier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use an authentication service. There are many different authentication services available, both free and paid. Authentication services can save you the hassle of having to implement and maintain your authentication system.&lt;/li&gt;
&lt;li&gt;Use a framework or library that provides authentication functionality. There are many different frameworks and libraries available that provide authentication functionality. Using a framework or library can save you the time and effort of having to implement authentication yourself. If you are ready to get started check out our &lt;a href="https://dev.to/docs/quickstarts/"&gt;FusionAuth Quickstarts&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Consider that when Better Napster, wait I mean your app, starts to grow, you will want an authentication provider that can handle different devices and form factors such as mobile, desktop, and web clients.&lt;/li&gt;
&lt;li&gt;Develop a local instance or self-contained environment that you can host yourself to make development easier.
&lt;/li&gt;
&lt;li&gt;Look to your favorite framework provider and see what they are using and use an authentication framework built for that.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What You &lt;em&gt;Should&lt;/em&gt; Care About
&lt;/h2&gt;

&lt;p&gt;Now that you have authentication figured out you can start to focus on the real requirements of building an application...&lt;/p&gt;

&lt;p&gt;Picking the rest of your software components:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Framework - &lt;a href="https://remix.run/" rel="noopener noreferrer"&gt;Remix&lt;/a&gt;, &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, &lt;a href="https://kit.svelte.dev/" rel="noopener noreferrer"&gt;SvelteKit&lt;/a&gt;, &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Pheonix Framework&lt;/a&gt;, &lt;a href="https://laravel.com/" rel="noopener noreferrer"&gt;Laravel&lt;/a&gt;, &lt;a href="https://flask.palletsprojects.com/en/3.0.x/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt;, &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Database - &lt;a href="https://planetscale.com/" rel="noopener noreferrer"&gt;PlanetScale&lt;/a&gt;, &lt;a href="https://cloud.google.com/alloydb" rel="noopener noreferrer"&gt;AlloyDB for PostgreSQL&lt;/a&gt;, &lt;a href="https://aws.amazon.com/rds/aurora/serverless/" rel="noopener noreferrer"&gt;AWS Aurora Serverless&lt;/a&gt;, &lt;a href="https://www.cockroachlabs.com/blog/announcing-cockroachdb-serverless/" rel="noopener noreferrer"&gt;CockroachDB Serverless&lt;/a&gt;, &lt;a href="https://www.edgedb.com/" rel="noopener noreferrer"&gt;EdgeDB&lt;/a&gt;, &lt;a href="https://neon.tech/" rel="noopener noreferrer"&gt;Neon&lt;/a&gt;, &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;, &lt;a href="https://www.timescale.com/" rel="noopener noreferrer"&gt;Timescale&lt;/a&gt;, &lt;a href="https://upstash.com/" rel="noopener noreferrer"&gt;Upstash&lt;/a&gt;, &lt;a href="https://grafbase.com/" rel="noopener noreferrer"&gt;Grafbase&lt;/a&gt;, &lt;a href="https://turso.tech/" rel="noopener noreferrer"&gt;Turso&lt;/a&gt;, &lt;a href="https://fauna.com/" rel="noopener noreferrer"&gt;Fauna&lt;/a&gt;, &lt;a href="https://ably.com" rel="noopener noreferrer"&gt;Ably&lt;/a&gt;, &lt;a href="https://www.convex.dev/" rel="noopener noreferrer"&gt;Convex&lt;/a&gt;, &lt;a href="https://hasura.io/" rel="noopener noreferrer"&gt;Hasura&lt;/a&gt;, &lt;a href="https://liveblocks.io/" rel="noopener noreferrer"&gt;liveblocks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Hosting - &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;, &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;, &lt;a href="https://fly.io/" rel="noopener noreferrer"&gt;Fly.io&lt;/a&gt;, &lt;a href="https://www.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CMS: &lt;a href="https://www.contentful.com/" rel="noopener noreferrer"&gt;Contentful&lt;/a&gt;, &lt;a href="https://www.quintype.com/" rel="noopener noreferrer"&gt;Quintype&lt;/a&gt;, &lt;a href="https://hygraph.com/" rel="noopener noreferrer"&gt;Hygraph&lt;/a&gt;, &lt;a href="https://ghost.org/publishers/" rel="noopener noreferrer"&gt;Ghost&lt;/a&gt;, &lt;a href="https://strapi.io/" rel="noopener noreferrer"&gt;Strapi&lt;/a&gt;, &lt;a href="https://www.sanity.io/" rel="noopener noreferrer"&gt;Sanity&lt;/a&gt;, &lt;a href="https://kontent.ai/" rel="noopener noreferrer"&gt;Kontent&lt;/a&gt;, &lt;a href="https://www.optimizely.com/products/orchestrate/content-management/" rel="noopener noreferrer"&gt;Optimizely&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are full backend solutions out there like &lt;a href="https://firebase.google.com/" rel="noopener noreferrer"&gt;Firebase&lt;/a&gt;, &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;, and &lt;a href="https://www.appwrite.io" rel="noopener noreferrer"&gt;Appwrite&lt;/a&gt;. However, if you don't like some part of these stacks then it becomes very difficult for you to decouple your application(s). In this video our Founder and CEO talks about &lt;a href="https://youtu.be/SLc3cTlypwM?si=qFzJaeA9NTeXGSfz&amp;amp;t=834" rel="noopener noreferrer"&gt;Authentication as a Microservice&lt;/a&gt; and how to use a JWT in your APIs.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;Okay, now that I got that out of my system I am off to listen to &lt;a href="https://youtu.be/UekfKh59KOw?si=xTM0powkicUHG2Yo" rel="noopener noreferrer"&gt;Connor Price&lt;/a&gt; and log in to Amazon using my &lt;a href="https://www.aboutamazon.com/news/retail/amazon-passwordless-sign-in-passkey" rel="noopener noreferrer"&gt;face&lt;/a&gt; because it is 2023 people!&lt;/p&gt;

&lt;p&gt;Oh yeah and now the actual coding begins!!&lt;/p&gt;

&lt;p&gt;If you want authentication to just work start using &lt;a href="https://dev.to/pricing"&gt;FusionAuth today for free&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Want to get jump-started using your framework of choice? Check out our &lt;a href="https://dev.to/docs/quickstarts/"&gt;Quickstarts&lt;/a&gt;.  &lt;/p&gt;

</description>
      <category>community</category>
      <category>authentication</category>
      <category>nextjs</category>
      <category>auth</category>
    </item>
    <item>
      <title>Authentication Workflows Overview</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Mon, 16 Oct 2023 15:39:32 +0000</pubDate>
      <link>https://forem.com/fusionauthio/authentication-workflows-overview-38ib</link>
      <guid>https://forem.com/fusionauthio/authentication-workflows-overview-38ib</guid>
      <description>&lt;p&gt;The landscape of applications today is broad. Similarly, the methods of authentication used is similarly varied. This section covers some of the login and authentication workflows used by applications today. These examples use FusionAuth as the IdP (identity provider), but any IdP could be used. You can use these articles to help architect and design the authentication system for your application.&lt;/p&gt;

&lt;p&gt;Keep in mind that this list is not exhaustive. Most IdPs, including FusionAuth, are capable of other forms of login. Also, there are many login workflows that we do not cover here due to security concerns with those methods.&lt;/p&gt;

&lt;p&gt;Also, these articles do not discuss the pros and cons of the different types of applications and which might be best for your needs. They focus entirely on authentication for applications. There are many articles available that cover different application types and why you should choose one over the other (such as the decision to use a native mobile application versus a responsive web application).&lt;/p&gt;

&lt;h2&gt;
  
  
  Definitions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native login form&lt;/strong&gt; - this is a form built directly into the application rather then leveraging an external login form such as an OAuth provider&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application backend&lt;/strong&gt; - this is the backend of the application, not the IdP (i.e. not FusionAuth)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Traditional web application authentication
&lt;/h2&gt;

&lt;p&gt;Traditional web applications (webapps) use the request and response nature of the HTTP protocol along with the URL address of the browser to provide functionality to users. Many new web applications are still implemented using this method and many existing web applications use this pattern.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bGzhw4oj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/img/articles/login-authentication-workflows/login-type-get-post.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bGzhw4oj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/img/articles/login-authentication-workflows/login-type-get-post.png" alt="Login Workflows" width="530" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These applications load a web page by making a request from the browser to the server based on the URL address. The server responds to these requests with HTML, CSS &amp;amp; JavaScript. These documents are then rendered by the browser.&lt;/p&gt;

&lt;p&gt;When the user clicks a link, button or submits a form, the browser makes a new request to the server and changes the URL in the address bar. The server handles the request and returns HTML, CSS &amp;amp; JavaScript that is rendered by the browser again. The browser renders the new documents.&lt;/p&gt;

&lt;p&gt;There are only two HTTP methods supported by the browser in this style of application, &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt;. The HTTP method informs the server what type of action the user took and how to handle the request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Single-page application authentication
&lt;/h2&gt;

&lt;p&gt;Single-page web applications (SPA sometimes pronounced spa or S-P-A) use a single request and response to download the application. Sometimes the entire application is retrieved on the initial request and other times only part of the application is retrieved. This initial retrieve consists of HTML, CSS and JavaScript that comprise the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eQz8Kt3p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/img/articles/login-authentication-workflows/login-type-xmlhttprequest.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eQz8Kt3p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/img/articles/login-authentication-workflows/login-type-xmlhttprequest.png" alt="Login Workflows" width="530" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the application is retrieved, it is started by the browser, normally by invoking a JavaScript framework that bootstraps and then begins to render the application. In many cases, the HTML of the application is produced by JavaScript and the browser updates the current document dynamically.&lt;/p&gt;

&lt;p&gt;SPAs use JavaScript and call APIs to handle user interactions and input. For example, if a user clicks a link, the SPA might modify the document to render a different page or form. When a user submits a form, the SPA might call an API on the server.&lt;/p&gt;

&lt;p&gt;APIs are invoked using the &lt;code&gt;XMLHttpRequest&lt;/code&gt; functionality of browser JavaScript. This generally makes calls to REST APIs on the server. When the server responds, the JavaScript running in the browser handles the response and updates the document.&lt;/p&gt;

&lt;p&gt;Since the browser is using &lt;code&gt;XMLHttpRequest&lt;/code&gt;, it can invoke all of the standard HTTP methods including &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt;. The HTTP method helps inform the server what type of action the user took and how to handle the request.&lt;/p&gt;

&lt;p&gt;It is important to note that while we recommend OAuth for SPAs, it causes the browser to close the SPA and navigate to the OAuth provider. Once the OAuth workflow is complete, the browser retrieves and starts the SPA again. This process might seem somewhat heavy, but the SPA should be cached in the browser making the second startup process much faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Native mobile application authentication
&lt;/h2&gt;

&lt;p&gt;Native mobile applications are usually installed via a store and installed on the mobile device (phone, tablet, etc). These applications are started by clicking the icon on the device. The device operating system then starts the application. Once the application is started, it renders its user interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wUqSN2df--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/img/articles/login-authentication-workflows/login-type-native.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wUqSN2df--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/img/articles/login-authentication-workflows/login-type-native.png" alt="Login Workflows" width="476" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Native applications often call APIs to handle user interactions and input. For example, if a user clicks a button or submits a form, the application might call an API on the server. This API might be called via HTTP or some other type of protocol. Often, native applications use various libraries for making API calls simpler.&lt;/p&gt;

&lt;p&gt;Some experts recommend that native applications (including mobile apps) use OAuth's authorization code grant. This method works fine with many IdPs, including FusionAuth, but is not listed in this section because it is covered in the SPA and WebApp sections above. The only difference is that at the end of the OAuth workflow, the native application pulls the JWT and refresh tokens from the web-view.&lt;/p&gt;

</description>
      <category>auth</category>
      <category>spa</category>
      <category>login</category>
      <category>idp</category>
    </item>
    <item>
      <title>Hacktoberfest 2023 with FusionAuth</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Wed, 04 Oct 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/fusionauth/hacktoberfest-2023-with-fusionauth-56g4</link>
      <guid>https://forem.com/fusionauth/hacktoberfest-2023-with-fusionauth-56g4</guid>
      <description>&lt;p&gt;We're excited to announce FusionAuth's participation in this year's Hacktoberfest! Hacktoberfest is a global event that takes place every October, during which developers are encouraged to contribute to open-source projects like FusionAuth. The goal is to foster a vibrant open-source community, celebrate shared knowledge, and make the world of coding more accessible to all. You can find other participating repositories by searching the &lt;a href="https://github.com/topics/hacktoberfest" rel="noopener noreferrer"&gt;hacktoberfest topic&lt;/a&gt; on github.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;The first step is to &lt;a href="https://hacktoberfest.com/register" rel="noopener noreferrer"&gt; register for Hacktoberfest&lt;/a&gt;. Once you've registered, any PRs submitted to FusionAuth will count towards your participation in the event!&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;If you haven't participated before make sure to read the &lt;a href="https://hacktoberfest.com/participation/%20" rel="noopener noreferrer"&gt; Hacktoberfest Participation Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To see all of FusionAuth's issues in one place see the &lt;a href="https://github.com/search?q=user%3AFusionAuth+label%3Ahacktoberfest+state%3Aopen&amp;amp;type=Issues&amp;amp;ref=advsearch&amp;amp;l=&amp;amp;l="&gt;full issue search&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This year for the Hacktoberfest we choose to focus on all of our JavaScript repositories. I have added the tag &lt;code&gt;hacktoberfest&lt;/code&gt; to each one of the following repositories and even added an issue for simply updating the packages to their latest version. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/fusionauth/fusionauth-quickstart-javascript-angular-web"&gt;Angular&lt;/a&gt;- The Authorization Code grant using the Angular framework&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-express-consents"&gt;Consents&lt;/a&gt;- Example using advanced registration forms and consents&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-node-deeplink"&gt;Deeplinking&lt;/a&gt;- Example returning users to the same page they logged in on&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fusionauth/fusionauth-example-device-grant"&gt;Device grant&lt;/a&gt;- An example of the Device Authorization grant&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-express-api"&gt;Express API&lt;/a&gt;- Express api which uses the Cookie Access Token&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fusionauth/fusionauth-example-family"&gt;Family management&lt;/a&gt;- Family management and consent creation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-node-sso"&gt;FusionAuth SSO&lt;/a&gt;- Example of SSO between two different custom applications&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-gaming-device-grant"&gt;Gaming and device grant&lt;/a&gt;- Example using the Device Authorization grant to provide authentication to a game.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fusionauth/fusionauth-example-gatsby"&gt;Gatsby OAuth&lt;/a&gt;- An example of using Gatsby with the Authorization Code grant and PKCE&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-node-services-gateway-jwtauth"&gt;JWT Auth and a Microservices gateway&lt;/a&gt;- API gateway and microservices secured using JWT auth&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fusionauth/fusionauth-example-javascript-jwt"&gt;Javascript JWT&lt;/a&gt;- JWT creation and decoding examples with javascript&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-nextjs-magic-links"&gt;Magic links login&lt;/a&gt;- nextjs app which uses magic links for authentication&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-node-services-gateway"&gt;Microservices gateway&lt;/a&gt;- API gateway and microservices&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-node-multi-tenant"&gt;Multi-tenant application&lt;/a&gt;- Two nodejs applications in different tenants, living in different domains.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-nextjs-single-sign-on"&gt;Next.js Single Sign-On&lt;/a&gt;- Single sign-on with Next.js and FusionAuth&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fusionauth/fusionauth-example-node"&gt;Node OAuth&lt;/a&gt;- Login with the Authorization Code grant&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fusionauth/fusionauth-example-react-native-0-71"&gt;React Native&lt;/a&gt;- The Authorization Code grant for a React Native mobile application&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fusionauth/fusionauth-example-react-sdk"&gt;React with an Express backend&lt;/a&gt;- The Authorization Code grant using the React framework with the FusionAuth React SDK and an Express backend&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fusionauth/fusionauth-quickstart-javascript-react-web"&gt;React with the hosted backend&lt;/a&gt;- The Authorization Code grant using the React framework with the FusionAuth React SDK and the hosted backend&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-remix"&gt;Remix auth&lt;/a&gt;- Example using FusionAuth to provide auth for a Remix application&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-express-twitter"&gt;Twitter login&lt;/a&gt;- Node/express app which uses Twitter for authentication&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/FusionAuth/fusionauth-example-user-actions-guide"&gt;User actions example&lt;/a&gt;- Corresponds to the user actions guide tutorial&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fusionauth/fusionauth-quickstart-javascript-vue-web"&gt;Vue.js&lt;/a&gt;- The Authorization Code grant using the Vue.js framework&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>How to set up an anonymous user flow</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Mon, 02 Oct 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/fusionauth/how-to-set-up-an-anonymous-user-flow-1h58</link>
      <guid>https://forem.com/fusionauth/how-to-set-up-an-anonymous-user-flow-1h58</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gV6L_P2r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2g8xfqx2qibzybx6dyh0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gV6L_P2r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2g8xfqx2qibzybx6dyh0.png" alt="How to set up an anonymous user flow" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For many applications, a user is forced to register for application access. You want to know who the user is so you can provide them with individualized functionality and data.&lt;/p&gt;

&lt;p&gt;If your registration process requires an email or mobile phone number, registration allows you to market to your customers. This is great if you are pursuing a Product-led growth or freemium business model. Having contact information allows you to communicate with them directly, allowing you to remind them of the value of your application and driving re-engagement.&lt;/p&gt;

&lt;p&gt;But sometimes, you don't want any registration friction, even though you want the user to access customized functionality or save data on your system. Anonymous users can help with this. Such users don't have any credentials or personally identifiable data associated with their accounts. They do exist in your customer identity and access management (CIAM) system and can be treated like any other account in terms of profile data or reporting. &lt;/p&gt;

&lt;p&gt;FusionAuth doesn't support this concept using &lt;a href="https://fusionauth.io/docs/v1/tech/core-concepts/integration-points#hosted-login-pages"&gt;hosted login pages&lt;/a&gt;. The hosted login pages abide by the 80/20 rule. They cover 80% of the use cases for customer identity and access management.&lt;/p&gt;

&lt;p&gt;When the hosted login pages don't meet your needs, you can use the &lt;a href="https://fusionauth.io/docs/v1/tech/apis/"&gt;APIs&lt;/a&gt; to build your workflows. You can do this for anonymous users. Here's a &lt;a href="https://fusionauth.io/docs/v1/tech/guides/anonymous-user"&gt;guide to implement this custom registration flow&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The rest of this blog post will look at some of the wrinkles of building out this progressive registration process, whether you use FusionAuth or any other system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anonymous User Lifecycle
&lt;/h2&gt;

&lt;p&gt;Anonymous users have a more complex life cycle than the typical CIAM user. Here's a typical lifecycle.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A person visits your application. This person is called a visitor or unknown user and is most likely tracked by analytics or web stats logging software.&lt;/li&gt;
&lt;li&gt;The visitor may take multiple actions such as viewing pages.&lt;/li&gt;
&lt;li&gt;The person takes a certain action in your application that requires the creation of a profile.&lt;/li&gt;
&lt;li&gt;An anonymous account is created to capture profile data. This is also known as a stub or shadow account.&lt;/li&gt;
&lt;li&gt;The person continues to take additional actions in the application. Some of these may trigger updates to the anonymous profile, others may not.&lt;/li&gt;
&lt;li&gt;At some time, the user registers for a conventional account. They may be prompted to do so based on activity, such as access to advanced functionality or an action that impacts the real world. Or they may choose to register.&lt;/li&gt;
&lt;li&gt;The person registers and the account converts to a normal user account, including all extant profile data.&lt;/li&gt;
&lt;li&gt;The person receives an email or text to set up their account.&lt;/li&gt;
&lt;li&gt;The person sets up their credentials, including password and/or additional factors.&lt;/li&gt;
&lt;li&gt;The person continues on their merry way, interacting with the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application benefits by having a lower time to value. The user benefits because they have access to functionality or preferences before they register, as well as retaining access to any such data after registration.&lt;/p&gt;

&lt;p&gt;As mentioned above, this is an example of progressive registration, with some profile data gathering taking place while the identity is unknown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Anonymous Users Make Sense
&lt;/h2&gt;

&lt;p&gt;There are two broad scenarios where anonymous users make sense. &lt;/p&gt;

&lt;p&gt;First, if you want to serve personalized content or recommendations to a user who has yet to identify themself.&lt;/p&gt;

&lt;p&gt;You might do this to increase the usefulness of your site. For example, for an  e-commerce site, you might allow the user to "favorite" items they are interested in. You could then surface related items, or items that other similar users found interesting. Another example might be an online game. Users might want to tweak how their character looks without bothering to register, and such tweaking might drive more gameplay.&lt;/p&gt;

&lt;p&gt;Second, if you want a user to have provided information or preferences available after registration.&lt;/p&gt;

&lt;p&gt;You might do this if you want to allow users to experience application functionality without the friction of signing up. For instance, a diagramming application might allow anonymous users to create diagrams. Users would welcome having their saved diagrams available after they have registered. In the above  e-commerce example, the favorited items should be available for perusal (and purchase!) after user registration.&lt;/p&gt;

&lt;p&gt;Anonymous accounts can be helpful across a wide variety of business domains, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; E-commerce sites with a shopping cart&lt;/li&gt;
&lt;li&gt;B2B applications where users can create software artifacts&lt;/li&gt;
&lt;li&gt;games&lt;/li&gt;
&lt;li&gt;high value research applications, such as real estate search sites &lt;/li&gt;
&lt;li&gt;news and content sites where personalization can drive engagement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, anonymous accounts aren't always a good option. They don't make sense:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;for applications where anonymous users don't make sense, such as an email or banking application&lt;/li&gt;
&lt;li&gt;if the profile data of the anonymous user is not valuable to the application or the user&lt;/li&gt;
&lt;li&gt;for applications where you must be able to contact all users&lt;/li&gt;
&lt;li&gt;if the profile data is highly sensitive, such as medical information&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Subtleties
&lt;/h2&gt;

&lt;p&gt;There are some implementation concerns you should consider when building a system with anonymous users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Marking Accounts as Anonymous
&lt;/h3&gt;

&lt;p&gt;Make sure you have some way to identify anonymous users as such. In FusionAuth, you might use the &lt;code&gt;user.data&lt;/code&gt; field. Below is a screenshot of an anonymous user in the admin UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8j3i-MYb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/img/blogs/anonymous-user/anonymous-user-in-admin-screen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8j3i-MYb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/img/blogs/anonymous-user/anonymous-user-in-admin-screen.png" alt="An anonymous user account in the FusionAuth admin UI." width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Marking users in this way allows you to run analytics to understand user behavior. It also lets you remove inactive anonymous accounts.&lt;/p&gt;

&lt;p&gt;The string &lt;code&gt;DNUXUS8WIUXRGHQSNALTECZGD8IYZ7LN0X09RTTX2G9WMVFWJ6CF3T7HMCJWV3SF&lt;/code&gt; is a random username, since FusionAuth requires all users to have either a username or an email address. &lt;/p&gt;

&lt;h3&gt;
  
  
  Storing The Anonymous User's Identity
&lt;/h3&gt;

&lt;p&gt;After you create an anonymous profile, you need to associate the person with that profile. With a web application, you can set a persistent cookie to maintain this association. The value of the cookie is typically a synthetic user Id, which does not identify the user outside of your system. A UUID is a good choice.&lt;/p&gt;

&lt;p&gt;You can store the Id in the following ways inside the cookie:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A plaintext value is easiest, but a malicious client can tweak the user Id to explore different users' saved data.&lt;/li&gt;
&lt;li&gt;A signed token, such as a JWT. Checking the signature on every request costs some computational power, but avoids malicious actors probing other user Ids.&lt;/li&gt;
&lt;li&gt;An encrypted, URL safe value. This is typically overkill for an opaque user Id, but if your user Id might leak information, such as the size of your user base because it is an integer value, then encryption might work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This cookie can be stored as an HTTPOnly, Secure cookie since only your server-side code will be examining it. Each time the cookie is presented, the server-side code can decode it, and then update the anonymous account profile if needed.&lt;/p&gt;

&lt;p&gt;Here's an example of code that sets a JWT with a user Id, using the &lt;a href="https://fusionauth.io/docs/v1/tech/apis/jwt#vend-a-jwt"&gt;FusionAuth JWT Vend API&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    # create a JWT, good for a year
    jwt_ttl=60*60*24*365
    jwt={
      'claims': {
        'userId': user_id
      },
      'keyId': env.get("SIGNING_KEY_ID"),
      'timeToLiveInSeconds': jwt_ttl
    }
    response = client.vend_jwt(jwt).success_response
    token=response['token']
    resp = make_response(render_template("video.html"))

    # set the cookie
    resp.set_cookie(ANON_JWT_COOKIE_NAME, token, max_age=jwt_ttl, httponly=True, samesite="Lax")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because you are storing information on the device, if a person accesses your site from a different browser or device, you have no way of reconciling them. This is also true if the user removes the cookie or the cookie expires. These are limitations of cookies, so there's no way around it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Converting Accounts
&lt;/h3&gt;

&lt;p&gt;When you convert an account, you associate the user Id stored in the cookie and a real-world user identifier such as an email address or a mobile phone number. Update the anonymous account with this information, which means it is no longer anonymous. Then send a "set up your password" request or other method of setting up credentials.&lt;/p&gt;

&lt;p&gt;Here's an example of code that does this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # if they have a cookie, look up the user and convert them and send a password reset
    if request.method == 'POST':
      user_id = get_anon_user_id_from_cookie()
      if user_id is None:
        print("couldn't find user")
        message["message"] = "Couldn't find your user id."
        return render_template("register.html", message=message)

      # correct the email address using patch if the email doesn't already exist
      email_param = request.form["email"]

      user = client.retrieve_user_by_email(email_param).success_response
      message["message"] = "Please check your email to set your password."

      # if we already have the user in our system, fail silently. depending on your use case, you may want to sent the forgot password email, or display an error message
      if user is None:
        patch_data = {
          'user': {
            'email': email_param
          }
        }
        patch_response = client.patch_user(user_id, patch_data).success_response

        forgot_password_data = {
          'loginId': email_param,
          'state': { 'anon_user': 'true' }
        }
        trigger_email_response = client.forgot_password(forgot_password_data)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verifying Accounts
&lt;/h3&gt;

&lt;p&gt;When converting an account from an anonymous account to a regular one, make sure you verify the user's ownership of the email address (or phone number) they provide. If you instead let someone provide an email address and password on the conversion page, anyone with device access would control the resulting profile.&lt;/p&gt;

&lt;p&gt;While that may be acceptable when an unknown user registers, where there is no anonymous user data, avoid letting someone "take over" an anonymous profile.&lt;/p&gt;

&lt;p&gt;One easy way to verify ownership is to send the password reset email or text after the user provides an email address or mobile phone number. At that point, you know they "own" it.&lt;/p&gt;

&lt;p&gt;Then you can update the associated profile to remove anonymous account indicators. Below you can see the code to do this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@app.route("/webhook", methods=['POST'])
def webhook():
  # look up the user by id. If they are not an anonymous user return 204 directly, otherwise update their anonymous user status to be false and return 204
  # looking for email user login event because email verified is only fired on explicit email verification
  if request.method == 'POST':
    webhookjson = request.json
    event_type = webhookjson['event']['type']
    is_anon_user = webhookjson['event']['user'] and webhookjson['event']['user']['data'] and webhookjson['event']['user']['data']['anonymousUser']
    if event_type == 'user.login.success' and is_anon_user:
      user_id = webhookjson['event']['user']['id']
      patch_data = {
        'user': {
          'username': '',
          'data' : {
            'anonymousUser':False
          }
        }
      }
      patch_response = client.patch_user(user_id, patch_data).success_response

  return '', 204
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In FusionAuth, the easiest way to do this is via a webhook, but different systems have different mechanisms. The important point is to leave the profile untouched until ownership is proven.&lt;/p&gt;

&lt;h3&gt;
  
  
  Culling Accounts
&lt;/h3&gt;

&lt;p&gt;At some point, clear out old anonymous accounts that have never converted to regular user accounts. Do this by querying the update timestamp of each anonymous account. Then, determine which untouched accounts are old enough to delete.&lt;/p&gt;

&lt;p&gt;Synchronize this with the lifetime of the cookie containing the user Id. If the cookie is good for a month, you can clear out accounts that have not been updated for 40 days, because the user would never be able to successfully convert their anonymous account after the cookie is gone.&lt;/p&gt;

&lt;p&gt;This reaping process can be run periodically by a scheduled job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy Concerns
&lt;/h3&gt;

&lt;p&gt;Ensure you abide by all rules for jurisdictions where your users live. In particular, make sure you don't collect &lt;a href="https://commission.europa.eu/law/law-topic/data-protection/reform/what-personal-data_en"&gt;any personal data&lt;/a&gt; unless you have the processes in place to abide by the GDPR.&lt;/p&gt;

&lt;p&gt;Personal data is defined as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;any information that relates to an identified or identifiable living individual. Different pieces of information, which collected together can lead to the identification of a particular person, also constitute personal data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Billing Concerns
&lt;/h3&gt;

&lt;p&gt;While the creation of anonymous accounts is unlikely to impact the system performance of an identity provider, these profiles may impact your bill. If your identity provider charges based on active users, you may be charged for these accounts. Before implementing this flow, make sure you understand what a large number of user accounts does to your pricing.&lt;/p&gt;

&lt;p&gt;For example, if you are using FusionAuth to implement this workflow, each anonymous account will be counted as an MAU for the month it is created, but not in subsequent months. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Just Use JavaScript
&lt;/h2&gt;

&lt;p&gt;You can definitely track anonymous actions with JavaScript, with a tool like Google Analytics. However, at some point, you will need to convert the profile to a server-side account with real credentials, so it may be easier to create it on the server side initially.&lt;/p&gt;

&lt;p&gt;In addition, if you want to have a single view of your user base, having all users in one data store will be easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;If you want to learn more about the anonymous user workflow, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read &lt;a href="https://fusionauth.io/docs/v1/tech/guides/anonymous-user"&gt;the anonymous user guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Run and tweak &lt;a href="https://github.com/FusionAuth/fusionauth-example-anonymous-user"&gt;an example anonymous user application&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Read &lt;a href="https://fusionauth.io/articles/ciam/unlocking-growth-low-friction-signup-process"&gt;"Unlocking Growth: Why A Low Friction User Signup Process Is Crucial For Your Business"&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>anonymousaccounts</category>
      <category>customloginworkflows</category>
      <category>jwts</category>
      <category>registration</category>
    </item>
    <item>
      <title>Get More Value Out of FusionAuth Using the APIs for User Maintenance</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Fri, 01 Sep 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/fusionauth/get-more-value-out-of-fusionauth-using-the-apis-for-user-maintenance-110n</link>
      <guid>https://forem.com/fusionauth/get-more-value-out-of-fusionauth-using-the-apis-for-user-maintenance-110n</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7xpZdxI8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gjyj4rrsmh9mgh109m3h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7xpZdxI8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gjyj4rrsmh9mgh109m3h.png" alt="Get More Out of FusionAuth Using APIs for User Maintenance" width="800" height="420"&gt;&lt;/a&gt;&lt;br&gt;
So, you have just set up FusionAuth and are excited to take your new &lt;a href="https://fusionauth.io/articles/ciam/"&gt;customer identity and access management&lt;/a&gt; platform out for a spin. You spend some time setting up your Users, Applications, Tenants, etc. in the robust admin interface provided. Things are looking great. You have integrated your applications and are humming along. Now comes a real test, user maintenance.&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“A new user with extra pickles, please.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Customizing the order for your favorite food can make a good meal better. Customizing your end-users' experience with user maintenance can make their experience with your application even better.&lt;/p&gt;

&lt;p&gt;For any &lt;a href="https://fusionauth.io/articles/security/steps-secure-your-authentication-system"&gt;secure application&lt;/a&gt;, user maintenance is a must. As a developer, you can often overlook the time and effort it takes to maintain the user's access and permissions for the shiny new application you created. In an ideal world, you do not want to be the ones to maintain the users. You want to give the task of user maintenance to the power users of the applications by making them application administrators. While those power users may be great at using the application, you don’t necessarily want to make them expert users of the FusionAuth admin interface. Well, we at FusionAuth would think it would be great if everyone was a master with it, but you may not share that same vision.&lt;/p&gt;

&lt;p&gt;Imagine you have an application that controls a machine that applies the toppings to a burger. Custom Burger, the popular new burger joint just bought your application. The tech-savvy manager, Sally Power-User, has been given admin rights. Sally just hired Joe Limited-Access to work the night shift. She now needs to add Joe as a user of your application. She needs to ensure Joe can add pickles to a burger order but make sure he does not have access to apply the famous Flaming Carolina Reaper Sauce until he goes through training. Your application will have to support this.&lt;/p&gt;

&lt;p&gt;You can harness the full power of FusionAuth in your application, hand off the user maintenance to the power users, and make it seamless for them. The application will need to ensure that Sally Power-User enters the correct information (&lt;strong&gt;Validation&lt;/strong&gt;), guide her through setting up the new Joe Limited-Access user correctly (&lt;strong&gt;Workflow&lt;/strong&gt;), and you want to make the process feel like it is part of your application (&lt;strong&gt;Branding&lt;/strong&gt;). You can accomplish all these through the use of the FusionAuth APIs.&lt;/p&gt;

&lt;p&gt;Newer developers should be aware the examples below are written with the assumption that you are familiar with how to make REST calls using the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-7.0"&gt;HttpClient&lt;/a&gt; in C#, the use of &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/"&gt;asynchronous&lt;/a&gt; calls and Windows Forms applications. In order for the sample application to run you must have a working &lt;a href="https://dev.to/docs/v1/tech/5-minute-setup-guide"&gt;FusionAuth server&lt;/a&gt;. Even if you are not totally familiar with these concepts, you will likely be able to follow the code snippets with general programming knowledge. If you need a refresher on some of these topics, there are some links at the end.&lt;/p&gt;

&lt;p&gt;If you are only interested in jumping into the code, it can be found &lt;a href="https://github.com/FusionAuth/fusionauth-example-dotnet-windowsform-api"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Custom Validation With FusionAuth APIs
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“With great power, comes great responsibility!”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of FusionAuth’s strengths is its flexibility. However, with the flexibility comes some complexity. To create a user in the FusionAuth admin user interface, you only need two pieces of information. You will need to supply either the username or the email in addition to a password. (If you are using multiple tenants, you will need the tenant ID as well.) The FusionAuth admin user interface gives you other optional fields to fill in, however none are required. When using the &lt;a href="https://fusionauth.io/docs/v1/tech/apis/users#create-a-user"&gt;Create a User API&lt;/a&gt;, only the username or email in addition to the password is still required but there are also over 30 additional request parameters available for customization. Creating a custom admin interface around the APIs will not only allow you to choose which of the username or email fields to use, but also what other meta-data is appropriate and required for your application. Below is sample code for creating a user using the FusionAuth API.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting Up The User Object
&lt;/h3&gt;

&lt;p&gt;First, you need to create a few objects to hold some values. By constructing the classes and decorating properties with the proper attributes, you can still use the strongly typed objects and standard naming conventions in your C# application while also using the JSON Serializer to easily format the request body when it is time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Newtonsoft.Json;

namespace FusionDemoAPI.Helper
{
  public class User
  {
    [JsonProperty(PropertyName = "user")]
    public UserProperties UserProperties;
  }

  public class UserProperties
  {
    [JsonProperty(PropertyName = "firstName")]
    public string FirstName;
    [JsonProperty(PropertyName = "lastName")]
    public string LastName;
    [JsonProperty(PropertyName = "email")]
    public string Email;
    [JsonProperty(PropertyName = "password")]
    public string Password;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Taking Input From The User
&lt;/h3&gt;

&lt;p&gt;Next, you need to take the input from the user and assign them to the values in your User object. In this case, you are performing those actions on a click event from the &lt;code&gt;Create User&lt;/code&gt; button on a &lt;a href="https://learn.microsoft.com/en-us/visualstudio/ide/create-csharp-winform-visual-studio?view=vs-2022"&gt;Windows Form&lt;/a&gt;. Pictured below is a screen shot of the interface for the sample application. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--69goAwR---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vb3ctewzao61ke75d18o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--69goAwR---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vb3ctewzao61ke75d18o.png" alt="The Windows demo front end." width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also take this chance to validate any information deemed relevant to your application. The logic in the &lt;code&gt;IsUserValid&lt;/code&gt; function can be edited to suit your needs. In this sample application it simply returns true. I know, not very creative, but useful in the demo. You can also validate age, location or anything else you choose. Once the validation is successful, you will then pass the user object to the method that will submit your request to the FusionAuth server. Validating input like this will ensure Sally Power-User enters the correct information for the system every time she adds a new employee.&lt;/p&gt;

&lt;p&gt;It is important to note that the &lt;code&gt;FusionAuthClient&lt;/code&gt; that has been created for this applications handles the setting of the &lt;a href="https://fusionauth.io/docs/v1/tech/apis/api-keys#overview"&gt;API Key&lt;/a&gt; and location of the FusionAuth server. These settings are stored in the &lt;code&gt;App.settings&lt;/code&gt; file for the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    private async void btnCreateUser_Click(object sender, EventArgs e)
    {
      //FusionAuthClient is a class that contains the logic to call the FusionAuth APIs
      var faClient = new FusionAuthClient();

      UserProperties userProperties = new UserProperties()
      {
        Email = txtEmail.Text,
        FirstName = txtFirstName.Text,
        LastName = txtLastName.Text,
        Password = txtPassword.Text
        //add additional properties here
      };

      User userToCreate = new User()
      {
        UserProperties = userProperties
      };

      //perform custom validation logic in the IsUserValid method
      if (IsUserValid(userToCreate))
      {
        ReturnValue userCreatedReturnValue = await faClient.CreateUser(userToCreate);
        if (userCreatedReturnValue.success == true)
        {
          LogResults($"User created with ID: {userCreatedReturnValue.result}");
          PopulateComboBox(cmbUsersToDelete, PopulateType.Users);
        }
        else
        {
          LogResults($"User creation failed. {userCreatedReturnValue.result}");
        }
      }
      else
      {
        LogResults($"User data provided is invalid");
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating The User
&lt;/h3&gt;

&lt;p&gt;Because you decorated your User objects correctly using the JSONProperty attribute, you can use the JSON Serializer to serialize the object with the &lt;code&gt;JsonConvert.SerializeObject&lt;/code&gt; method. You also need to encode it properly before you submit it. Once that is complete, you can submit the request to the FusionAuth server. You will do so here using the async pattern so as to not hold up the processing for a request that may take some time or give the application an unresponsive feel. Once you receive the return values, you can interrogate them and act accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public async Task&amp;lt;ReturnValue&amp;gt; CreateUser(User userToCreate)
    {
      //returns HttpClient with correct setting for instance
      HttpClient httpClient = GetClient();

      //initialize the return value 
      ReturnValue returnVal = new ReturnValue()
      {
        success = false
      };

      try
      {
        //set the endpoint for creating a user
        string apiURL = $"/api/user";

        var payload = JsonConvert.SerializeObject(userToCreate);
        var content = new StringContent(payload, Encoding.UTF8, "application/json");

        HttpResponseMessage response = await httpClient.PostAsync($"{FUSIONAUTH_HOST}{apiURL}", content);

        if (response.IsSuccessStatusCode)
        {

          string responseBody = await response.Content.ReadAsStringAsync();
          JObject result = JObject.Parse(responseBody);
          returnVal.success = true;
          //assign the newly created user id to the result
          returnVal.result = result["user"]["id"].ToString();
        }

      }
      catch (Exception ex)
      {
        Console.WriteLine($"There was an error in the request {ex.Message}");
        returnVal.result = ex.Message;
      }

      return returnVal;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  User Workflow With FusionAuth APIs
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“Amateurs do it till they get it right. Professionals do it until they can not get it wrong.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You may have originally set up the users manually in the FusionAuth admin user interface and that is fine to get started. However, the more steps and requirements you have for creating a user, the more likely something will be missed. There can be several steps required to set up a user correctly such as making sure they belong to the correct group(s). While you may be infallible as a developer and get it right every time, wink wink, most likely your users will not.&lt;/p&gt;

&lt;p&gt;You can use the FusionAuth APIs to ensure the user is added to the correct group or groups so their permissions will be set properly. After the user is created, you can take this opportunity to automatically assign the user to a specific group or present an admin interface to the administrator that will guide them in selecting the proper group(s). For example, you can ensure Sally Power-User cannot add Joe Limited-Access to the Flaming Carolina Reaper Sauce Appliers on the day she creates him as a user because you know he has not had time to go through the training.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up The Group Object
&lt;/h3&gt;

&lt;p&gt;Once again, you will need to create a few objects to help us organize your groups. The &lt;a href="https://fusionauth.io/docs/v1/tech/apis/groups#add-users-to-a-group"&gt;Add Users to a Group API&lt;/a&gt; call you are using requires a collection of groups that each contain an array of user information. In this case, you will only be using the user Id for the new GroupUser information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Newtonsoft.Json;
using System.Collections.Generic;

namespace FusionDemoAPI.Helper
{
  public class Group
  {
    public string ID;
    public List&amp;lt;GroupUser&amp;gt; Users;
  }

  public class GroupUser
  {
    [JsonProperty(PropertyName = "userId")]
    public string UserID;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Taking Input From The User
&lt;/h3&gt;

&lt;p&gt;As with creating the user, you need to take the input from the application and assign the values to your Group objects. Again, the &lt;code&gt;IsGroupValid&lt;/code&gt; function simply returns true for the sample application but you can also validate any data you want before actually adding the user to the group. After you are satisfied the user should be added, you can submit the information to the method that will submit your request to the FusionAuth server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    private async void btnAddUserToGroup_Click(object sender, EventArgs e)
    {
      //FusionAuthClient is a class that contains the logic to call  the FusionAuth APIs
      var faClient = new FusionAuthClient();
      //Get the selected values from the dropdowns
      var userIdToAdd = (string)cmbUsersToAddToGroup.SelectedValue;
      var groupIdToAdd = (string)cmbGroups.SelectedValue;

      if (userIdToAdd != null &amp;amp;&amp;amp; groupIdToAdd != null)
      {
        List&amp;lt;GroupUser&amp;gt; users = new List&amp;lt;GroupUser&amp;gt;()
                {
                    new GroupUser()
                    {
                        UserID = userIdToAdd
                    }
                };

        Group group = new Group();
        group.ID = groupIdToAdd;
        group.Users = users;

        List&amp;lt;Group&amp;gt; groups = new List&amp;lt;Group&amp;gt;();
        groups.Add(group);

        //perform custom validation logic here
        if (IsGroupValid(group))
        {
          ReturnValue addUserToGroupReturnValue = await faClient.AddUserToGroup(groups);
          if (addUserToGroupReturnValue.success == true)
          {
            LogResults($"User {(string)cmbUsersToAddToGroup.Text} has been added to the group: {(string)cmbGroups.Text}");
          }
          else
          {
            LogResults($"Adding the user to the group has failed. {addUserToGroupReturnValue.result}");
          }
        }
        else
        {
          LogResults($"User and/or group data provided is invalid");
        }
      }
      else
      {
        LogResults($"No group or user selected.  Populate Users and Groups.");
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding The User To The Group
&lt;/h3&gt;

&lt;p&gt;Because the JSON format expected in the request body is not readily serializable given the Group objects you have created, you will have to perform a little wizardry to manipulate the Group objects. While this does not have to do with FusionAuth specifically, the code has been abstracted in the &lt;code&gt;FormatGroupUserJSON&lt;/code&gt; function and can be found &lt;a href="https://github.com/FusionAuth/fusionauth-example-dotnet-windowsform-api/blob/35307a11e43caa098d3366be58b07cf0452b6488/sourcecode/FusionDemoAPI/FusionAuthClient.cs#L267-L295"&gt;here&lt;/a&gt; if you are interested. Once the API call has returned, you can retrieve whichever values you see as relevant and pass it back in the result object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public async Task&amp;lt;ReturnValue&amp;gt; AddUserToGroup(List&amp;lt;Group&amp;gt; groups)
    {
      //returns HttpClient with correct setting for instance
      HttpClient httpClient = GetClient();

      //initialize the return value 
      ReturnValue returnVal = new ReturnValue()
      {
        success = false
      };

      try
      {
        //set the endpoint for creating a user
        string apiURL = $"/api/group/member";

        var payload = FormatGroupUserJSON(groups);
        var content = new StringContent(payload, Encoding.UTF8, "application/json");

        HttpResponseMessage response = await httpClient.PostAsync($"{FUSIONAUTH_HOST}{apiURL}", content);

        if (response.IsSuccessStatusCode)
        {

          string responseBody = await response.Content.ReadAsStringAsync();
          JObject result = JObject.Parse(responseBody);
          returnVal.success = true;
          returnVal.result = $"Member ID for added user: {result["members"].First().First()[0]["id"]}";
        }
        else
        {
          returnVal.result = $"Adding user to the group failed.\nPayload\n{payload}\n{response}";
        }

      }
      catch (Exception ex)
      {
        Console.WriteLine($"There was an error in the request {ex.Message}");
        returnVal.result = ex.Message;
      }


      return returnVal;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Branding Your User Management With FusionAuth
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“Design is the silent ambassador of your brand.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of the best parts about using the APIs to allow application admins to manage their own users is they never have to leave your application. Users are accustomed to things such as Single Page Applications or at least applications that have a consistent look and feel. Depending on the use case, a user may feel uncomfortable switching to the FusionAuth admin interface to manage users. While FusionAuth has a well designed and functional admin interface, the look and feel will likely be different than your application. By using the APIs you can still use all of your own style sheets and design principles.&lt;/p&gt;

&lt;p&gt;You may ask yourself, “If I create an admin end for user management in my application, shouldn’t I just create the authentication and authorization framework as well?” The answer to that question is a resounding “No!” The security, power and flexibility FusionAuth gives you has taken years to build. You should focus on providing the unique business value your application provides versus reinventing the authentication wheel. The best part is that you can leverage our investment by creating a simple &lt;a href="https://fusionauth.io/articles/ciam/"&gt;customer identity and access management&lt;/a&gt; interface around the security and power of FusionAuth. You get the best of both worlds.&lt;/p&gt;

&lt;p&gt;You don’t even have to tell your users you are using FusionAuth. It’s okay, we don’t mind. We want to make you look good, help you secure your users, and let you focus on building the features your users want.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BrQMwe62--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/img/blogs/get-more-value/one-does-not-simply.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BrQMwe62--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/img/blogs/get-more-value/one-does-not-simply.png" alt="One does not simply implement user access management." width="651" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It is important to note that as an alternative, FusionAuth does allow the creation of &lt;a href="https://dev.to/docs/v1/tech/core-concepts/tenants#form-settings"&gt;custom forms&lt;/a&gt; in the admin user interface. This solution is only available for paid plans, may not support the workflow you are trying to accomplish and would not work if you did not want to give users access to the FusionAuth admin interface.&lt;/p&gt;

&lt;p&gt;These are a few examples of how to use the FusionAuth APIs to get more value out of FusionAuth. Please take some time to explore the &lt;a href="https://dev.to/docs/v1/tech/apis/"&gt;FusionAuth API documentation&lt;/a&gt; and get some more ideas of ways to use them to better serve your development. For instance, you have used the APIs to create a user, but you can use similar APIs to delete users or remove their application access. For example, you can monitor an external application and remove the user from FusionAuth on a given event.&lt;br&gt;
 By integrating the FusionAuth APIs not only can you automate the validation process, setup users, and make it all look like your own, but you can add almost any ability within FusionAuth to your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this article, please check out some of the other links and resources about FusionAuth.&lt;/p&gt;

&lt;h3&gt;
  
  
  FusionAuth APIs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fusionauth.io/docs/v1/tech/apis/"&gt;General API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fusionauth.io/docs/v1/tech/apis/users#create-a-user"&gt;Create a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fusionauth.io/docs/v1/tech/apis/users#delete-a-user"&gt;Delete a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fusionauth.io/docs/v1/tech/apis/groups#add-users-to-a-group"&gt;Add Users to a Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  FusionAuth Server Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fusionauth.io/docs/v1/tech/5-minute-setup-guide"&gt;5-Minute Setup Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sample Application Source
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/FusionAuth/fusionauth-example-dotnet-windowsform-api"&gt;FusionAuth API Demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  General
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-7.0"&gt;HttpClient&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/"&gt;Asynchronous programming with async and await&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>usermaintenance</category>
      <category>windows</category>
      <category>forms</category>
    </item>
    <item>
      <title>From ALB to Caddy - Our Wandering Path to Supporting Thousands of Domain Names</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Thu, 24 Aug 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/fusionauth/from-alb-to-caddy-our-wandering-path-to-supporting-thousands-of-domain-names-3gmj</link>
      <guid>https://forem.com/fusionauth/from-alb-to-caddy-our-wandering-path-to-supporting-thousands-of-domain-names-3gmj</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6MAUwukq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1quci56q6fy1imz3tfsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6MAUwukq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1quci56q6fy1imz3tfsd.png" alt="How We Built Unlimited Custom Domains" width="800" height="420"&gt;&lt;/a&gt;&lt;br&gt;
In the world of cloud services, offering 'unlimited' often feels like a stretch. But we've done just that. We recently unveiled support for unlimited custom domains in FusionAuth Cloud. This marked a significant milestone in our journey and changed the game for our customers.  Dive in as we unpack the architectural wizardry behind this transformative feature.&lt;/p&gt;

&lt;p&gt;When customers use our FusionAuth Cloud managed hosting service, they can configure one or more custom domain names for their authentication server.&lt;/p&gt;

&lt;p&gt;For example, a hosted deployment located at piedpiper.fusionauth.io could also be accessed at auth.piedpiper.com or id.piedpiper.com. This allows customers to keep their users on the same domain and maintain their branding throughout authentication flows.&lt;/p&gt;

&lt;p&gt;For most companies, one or two custom domains is sufficient. However some business models have thousands of domain names, representing different applications or customers. In an ideal world, the same authentication source would serve all of these domain names..&lt;/p&gt;

&lt;p&gt;FusionAuth supports &lt;a href="https://dev.to/docs/v1/tech/core-concepts/tenants"&gt;multiple tenants&lt;/a&gt; and applications, so this shouldn't be a problem, right?&lt;/p&gt;

&lt;p&gt;Right?&lt;/p&gt;

&lt;p&gt;Correct! This is not an issue for FusionAuth as a product!&lt;/p&gt;

&lt;p&gt;But things get a bit more complicated inside of &lt;a href="https://dev.to/platform/fusionauth-cloud"&gt;FusionAuth Cloud&lt;/a&gt;, our managed hosting option, which runs on Amazon Web Services (AWS).&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works Today
&lt;/h2&gt;

&lt;p&gt;Let's take a look at the architecture of FusionAuth Cloud, which includes both FusionAuth the product and surrounding infrastructure, called a deployment.&lt;/p&gt;

&lt;p&gt;Internet traffic flowing into FusionAuth Cloud first arrives via a Global Accelerator and is then forwarded to an &lt;a href="https://aws.amazon.com/elasticloadbalancing/application-load-balancer"&gt;Application Load Balancer&lt;/a&gt; (ALB) for routing. The ALB is responsible for handling the TLS handshake, the routing of traffic to customer deployments, as well as performing distributed denial of service (DDoS) protection and web application firewall (WAF) filtering. This ALB must be configured with a TLS certificate and a routing rule for each of our customer's domains.&lt;/p&gt;

&lt;p&gt;However, AWS imposes limitations on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The number of Subject Alternative Names (hostnames) per certificate&lt;/li&gt;
&lt;li&gt;  The number of certificates associated with an ALB&lt;/li&gt;
&lt;li&gt;  The number of hostname routing rules associated with an ALB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it impossible to use a single ALB to handle the traffic for all of our FusionAuth Cloud customers in a given region. In order to work around these limitations, we have provisioned additional ALBs and Global Accelerators. This also meant we imposed a limit on customers: they may only have four custom domains per deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's This Unlimited Domains Stuff Anyway?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.toblog/introducing-unlimited-custom-domains-for-fusionauth-cloud"&gt;Unlimited Domains&lt;/a&gt; is a new feature of FusionAuth Cloud that utilizes a modern edge infrastructure, providing dynamic TLS certificate provisioning and has no limits on the number of custom domains for a given deployment&lt;sup&gt;1&lt;/sup&gt;. It is currently an option available to select use cases for customers using High-Availability FusionAuth Cloud deployments.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt; Nothing is truly infinite, but we have your use-case covered!&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming Up With An Alternative
&lt;/h2&gt;

&lt;p&gt;Conceptually, the problem we were looking at is simple: We need to replicate the functionality of AWS's ALB, but without the limitations. Effectively, this should be implemented as a reverse-proxy that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Is highly-available across multiple &lt;a href="https://aws.amazon.com/about-aws/global-infrastructure/regions_az/"&gt;AWS Availability Zones&lt;/a&gt; (AZs)&lt;/li&gt;
&lt;li&gt;  Is horizontally scalable and can handle load as we grow&lt;/li&gt;
&lt;li&gt;  Can support dynamic management of TLS certificates without downtime&lt;/li&gt;
&lt;li&gt;  Has no limits on the number of hostnames it can route&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We decided to use AWS's Elastic Container Service (ECS) as our foundation, since it is fully managed and can automatically scale instances across AZs.&lt;/p&gt;

&lt;p&gt;For the reverse proxy, either HAProxy or nginx worked, but we needed to determine how we could reconfigure them at runtime to automatically provision certificates as customers added domain names. We spent some time researching their respective APIs to see how they would fit into the FusionAuth Cloud management infrastructure.&lt;/p&gt;

&lt;p&gt;During this research we stumbled upon &lt;a href="https://caddyserver.com/"&gt;Caddy&lt;/a&gt;, a software project that seemed to fit perfectly with our needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  It natively supports ACME, the certificate provisioning protocol used by LetsEncrypt.&lt;/li&gt;
&lt;li&gt;  It is happy running in a container.&lt;/li&gt;
&lt;li&gt;  It has an API for dynamic configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since Caddy was a new kid on the block, it required a more thorough investigation than the battle-tested incumbents.&lt;/p&gt;

&lt;p&gt;We set up a simulation environment to run it through its paces. Specifically, we wanted to ensure that when it was loaded up with 100k certificates, each with a unique hostname, it was able to perform TLS termination and route requests with throughput comparable to an ALB.&lt;/p&gt;

&lt;p&gt;We found that with minimal resources allocated to the container, there was no increase in request processing time as the number of installed certificates grew. We felt confident enough in Caddy to move forward with integrating it into a test environment which mirrored our production infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building It Out
&lt;/h2&gt;

&lt;p&gt;To avoid disruptions to our existing cloud deployments, we decided to build the new infrastructure in parallel with the existing systems.&lt;/p&gt;

&lt;p&gt;The new design consisted of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A multi-AZ ECS cluster of Caddy instances running on Fargate. This cluster would manage certificates, terminate TLS, and forward requests back to each customer deployment.&lt;/li&gt;
&lt;li&gt;  An EFS volume for Caddy to store certificates. Sometimes you just need a filesystem.&lt;/li&gt;
&lt;li&gt;  A Network Load Balancer (NLB) to distribute traffic across the Caddy instances.&lt;/li&gt;
&lt;li&gt;  A Global Accelerator to forward all edge traffic to the NLB.&lt;/li&gt;
&lt;li&gt;  An internal service which authorizes Caddy to provision certificates for only the domains that we explicitly allow.&lt;/li&gt;
&lt;li&gt;  Additional internal DNS records to map custom domain names to their respective deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This design was simple, but lacked one important component: a &lt;a href="https://aws.amazon.com/waf/"&gt;Web Application Firewall&lt;/a&gt; (WAF). We use AWS's WAF to protect our customers' deployments against DDoS and bot attacks.&lt;/p&gt;

&lt;p&gt;Since WAF can only inspect plaintext traffic and only works with ALBs, we had to insert a new ALB between Caddy and our customer deployments to get this protection. The alternative was running a WAF from a different vendor, which is possible but would balloon the scope of this project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ww5w3TGl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jay9nskibkyqw8rp8j7p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ww5w3TGl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jay9nskibkyqw8rp8j7p.png" alt="Unlimited Custom Domains Diagram" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Wrinkle With The WAF
&lt;/h2&gt;

&lt;p&gt;A WAF can also get in the way sometimes. Integrations requiring frequent API access occasionally trigger DDoS rules, which means legitimate traffic is blocked. Customers may wish to add certain static IP addresses to an allowlist, guaranteeing unfettered access to their authentication servers.&lt;/p&gt;

&lt;p&gt;To implement the allowlist, we need to know the source IP address of the traffic as it enters the Global Accelerator.&lt;/p&gt;

&lt;p&gt;Luckily for us, Global Accelerator has an option to preserve the client IP address, so Caddy can pass it through in an &lt;code&gt;X-Fowarded-For&lt;/code&gt; header where the WAF can compare it against our allow lists.&lt;/p&gt;

&lt;p&gt;Unluckily for us, this option is only &lt;a href="https://docs.aws.amazon.com/global-accelerator/latest/dg/preserve-client-ip-address.html"&gt;available&lt;/a&gt; when Global Accelerator is forwarding traffic to either an ALB or directly to EC2 instances. Since using an ALB here would re-introduce the limitations that we're trying to workaround, we were forced to ditch Fargate and use bare EC2 instances as our ECS capacity provider.&lt;/p&gt;

&lt;p&gt;Running ECS workloads on EC2 is not difficult, but does require us to build out the automation to deploy, scale and keep the instances up to date. The Global Accelerator's endpoint group must also remain in sync with these instances.&lt;/p&gt;

&lt;p&gt;These instances need to do only one thing: run Caddy containers. In order to minimize the attack surface, we did not want them running any services other than Docker. They do not need to run SSHD or most other services which are included in standard Linux distributions, so we searched for the most minimal distribution that would satisfy our basic requirements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/bottlerocket"&gt;Bottlerocket&lt;/a&gt; is one such distribution. It is maintained by AWS, includes only the necessities to run Docker containers and it easily integrates with ECS. After building out our new ECS cluster with Bottlerocket as a base image, we found that our Caddy containers were failing to start with only a cryptic error message pointing to a failed EFS mount.&lt;/p&gt;

&lt;p&gt;We verified that all of the IAM roles, security groups and file system policies were correct, but finally came across this &lt;a href="https://github.com/bottlerocket-os/bottlerocket/issues/1080"&gt;issue&lt;/a&gt;. Bottlerocket does not support authenticated EFS volumes, and probably never will.  &lt;/p&gt;

&lt;p&gt;The next best distribution turned out to be Amazon's ECS-optimized AMI. It wasn't as minimal as Bottlerocket, but with some tweaks we were able to lock it down and swap it into our ECS cluster. After this change, everything was working. From dynamic certificate provisioning to load balancing to deployment routing.&lt;/p&gt;

&lt;p&gt;At last, we had fully integrated Caddy end-to-end into our infrastructure. Our existing monitoring and alerting systems were easily attached to the new resources and we found no issues during our final testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means For FusionAuth Cloud Customers
&lt;/h2&gt;

&lt;p&gt;With this architecture change we can now support unlimited custom domains in FusionAuth Cloud. Whatever your use case -- we can handle it. All plans include a private instance (no shared public cloud) fully managed by FusionAuth, with options for backup, custom availability zones, uptime SLAs, and more to meet your infrastructure requirements.&lt;/p&gt;

&lt;p&gt;You can start a &lt;a href="https://dev.to/pricing?step%3Dhosting"&gt;free trial of FusionAuth Cloud&lt;/a&gt; now or for more complex projects or infrastructure questions, reach out to our &lt;a href="https://dev.to/contact"&gt;identity experts&lt;/a&gt; for help.&lt;/p&gt;

</description>
      <category>fusionauthcloud</category>
      <category>aws</category>
      <category>domains</category>
      <category>caddy</category>
    </item>
    <item>
      <title>Introducing Unlimited Custom Domains for FusionAuth Cloud</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Tue, 25 Jul 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/fusionauth/introducing-unlimited-custom-domains-for-fusionauth-cloud-ebh</link>
      <guid>https://forem.com/fusionauth/introducing-unlimited-custom-domains-for-fusionauth-cloud-ebh</guid>
      <description>&lt;p&gt;Today, we’re announcing Unlimited Custom Domains for &lt;a href="https://fusionauth.io/platform/fusionauth-cloud"&gt;FusionAuth Cloud&lt;/a&gt;. Custom domains allow you to associate another domain with your FusionAuth Cloud deployment without having to fuss with certificate issuance and renewal – it just works.&lt;/p&gt;

&lt;p&gt;Let’s take a look at the changes now and stay tuned for a future blog on how we engineered this feature (hint - it involves &lt;a href="https://caddyserver.com/"&gt;Caddy&lt;/a&gt;, an open-source project we are proud to support).&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Custom Domains?
&lt;/h2&gt;

&lt;p&gt;Custom domains let you brand your login experience and have your users see a domain they recognize – yours. Instead of seeing &lt;code&gt;piedpiper.fusionauth.io&lt;/code&gt; in your login page’s URL, adding a custom domain allows you to set it as &lt;code&gt;auth.piedpiper.com.&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But Wait, Custom Domains Aren’t New
&lt;/h2&gt;

&lt;p&gt;That’s true! Custom domains have been available for FusionAuth Cloud since 2021, but we supported a maximum of 4 custom domains, even on our High Availability hosting plan. Adding more than that required using a &lt;a href="https://fusionauth.io/docs/v1/tech/admin-guide/proxy-setup"&gt;proxy&lt;/a&gt; layer like CloudFlare, Apache, or CloudFront and extra work for FusionAuth Cloud customers to manage those domains and certificates.&lt;/p&gt;

&lt;p&gt;That got us thinking… why limit it to 4? And how could we streamline the process for customers?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So as of today, these changes are now in effect for FusionAuth Cloud:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High Availability Hosting now includes Unlimited Custom Domains.&lt;/li&gt;
&lt;li&gt;A streamlined user interface for managing custom domains within the FusionAuth admin portal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---J5maFUk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/unlimited-custom-domains/fusionauth-cloud-custom-domains.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---J5maFUk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/unlimited-custom-domains/fusionauth-cloud-custom-domains.png" alt="Introducing Unlimited Custom Domains." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Add a Custom Domain
&lt;/h2&gt;

&lt;p&gt;If you are on a supported deployment and want a custom domain name such as &lt;code&gt;auth.piediper.com&lt;/code&gt;, it’s now entirely self-service. Log in to your account portal, navigate to &lt;strong&gt;Deployments&lt;/strong&gt; and then select &lt;strong&gt;Custom URL(s)&lt;/strong&gt; under the &lt;strong&gt;Action&lt;/strong&gt; dropdown. Check out the custom domains &lt;a href="https://dev.to/docs/v1/tech/installation-guide/cloud#unlimited-custom-domains"&gt;documentation&lt;/a&gt; for details about how to add the domain name and update an existing domain record.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CYA4v_rl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/unlimited-custom-domains/managing-domains-fusionauth-cloud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CYA4v_rl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/unlimited-custom-domains/managing-domains-fusionauth-cloud.png" alt="Managing a Custom Domain." width="800" height="799"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other customer identity and access management platforms typically only offer one custom domain as standard. If you need more than that, some vendors even make you pay an extra fee per domain. With FusionAuth, it’s just another feature we are adding to support our &lt;a href="https://dev.to/blog/2023/03/15/winning-customer-auth-market"&gt;growing customer base&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;FusionAuth Cloud offers different hosting tier selections to give you a choice for your deployment needs. All plans include a private instance (no shared public cloud) fully managed by FusionAuth, with options for backup, custom availability zones, uptime SLAs, and more to meet your infrastructure requirements.&lt;/p&gt;

&lt;p&gt;You can start a &lt;a href="https://fusionauth.io/pricing?step=hosting"&gt;free trial of FusionAuth Cloud&lt;/a&gt; now or for more complex projects or infrastructure questions, reach out to our &lt;a href="https://fusionauth.io/contact"&gt;identity experts&lt;/a&gt; for help.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Contenda used FusionAuth instead of rolling their own auth stack and saved "a lot" of time</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Mon, 17 Jul 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/fusionauth/contenda-used-fusionauth-instead-of-rolling-their-own-auth-stack-and-saved-a-lot-of-time-2emf</link>
      <guid>https://forem.com/fusionauth/contenda-used-fusionauth-instead-of-rolling-their-own-auth-stack-and-saved-a-lot-of-time-2emf</guid>
      <description>&lt;p&gt;Tamás Deme is a FusionAuth community member and engineering lead at Contenda. Grady Salzman is a FusionAuth community member and software engineer at Contenda. They chatted with us over email about how they and their team are using FusionAuth to meet their auth needs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This interview has been lightly edited for clarity and length.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; Can you tell me a bit about Contenda? What is the company's mission?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; Contenda is a content creation tool designed to enable anyone on a team to take any video, such as a conference talk, interview, or demo, and turn it into effective written content. It features an easy-to-use upload, transformation, and customization system, a powerful API to automate content production pipelines, and is built specifically for developer advocates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; What are some examples of features/functionality that are useful specifically for developer advocates?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; Our platform is capable of grabbing and re-formatting code-snippets from videos, and we made sure our editor uses and works well with Markdown, which is generally preferred by the dev advocate community.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; What is the company's mission?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; To democratize and increase quality written content creation for users &amp;amp; organizations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[Auth is] a gigantic headache to do on your own; it's easy to get suffocated in a sea of edge cases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; Tell me more about democratization. Can't anyone write good content? (Thank you, Tim Berners-Lee!) What am I missing?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; We believe that creating technical content should be accessible to everyone, no matter their experience level. What's "good" is not necessarily clear cut, as at minimum it starts a discussion and provides an understanding of everyone's point of view, which can then facilitate learning and correcting any misconceptions.&lt;/p&gt;

&lt;p&gt;When we talk about quality we think about how accurately we can represent the original content we process, and make sure we don't make up things the original creator never intended to say.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; Tell me about your work as engineers at Contenda.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grady:&lt;/strong&gt; I am the front end engineer, and I am responsible for building out our web app. For this integration, I did anything that wasn't backend. This included implementing the FusionAuth themes and hooking FusionAuth up to our web app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás:&lt;/strong&gt; My job as the engineering lead is to drive the architectural, infrastructure and backend efforts at the company, and generally help the other engineers grow as the company grows.&lt;/p&gt;

&lt;p&gt;Specifically for this integration process, I've mostly done everything that wasn't the frontend - starting with analyzing and choosing an auth provider, to performing the required database and backend changes, and handling the migration and support now that we've launched.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; How do you use FusionAuth? OAuth? User management? Social sign-on? Something else?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; We use FusionAuth for almost all of our user management needs, except maybe for our workspace management. This includes doing authentication for our web app and our API. FusionAuth handles standard email/password sign ups and social sign ups as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; For your API, are you using the client credentials grant, or a JWT from the authorization code grant or something else?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; Right now we're using the &lt;a href="https://dev.to/docs/v1/tech/tutorials/application-authentication-tokens"&gt;Application Authentication Token&lt;/a&gt; feature/API to get JWTs, although we might change that up in the future.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We appreciate that FusionAuth doesn't have any "trap-like" pricing practices that could bite us when we happen to approach an arbitrary number of users.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; From a user management perspective, do you interact with the FusionAuth admin UI or the APIs plus a custom app?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; Yes, that's mostly correct. Our own tools for our own backend aren't as sophisticated just yet, but we'll get there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; What problems did we solve for you?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás:&lt;/strong&gt; To sum up, all of auth. Unless you're a major player (and sometimes even then), rolling your own auth stack is probably a bad idea, so we've always been planning on looking for a reliable 3rd party provider to handle all this for us. It's a gigantic headache to do on your own; it's easy to get suffocated in a sea of edge cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; How were you solving them before FusionAuth?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; In the past we've used a very bare bones, API key only sign-up system we put together in under a week.&lt;/p&gt;

&lt;p&gt;While it served us getting our first 1000 users, we definitely needed something more robust, secure and user-friendly to get to the next magnitude of users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; What did you use for the first system? Was it framework based, a library, or something else?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; It was a completely custom implementation, just the very basics really.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We could've spent months working on a complex system, and any FusionAuth employee could still say "Look what they need to mimic a fraction of our power" after that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; Why did you choose FusionAuth over the alternatives?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; After we eliminated all the options we had to rule out due to technical limitations, the two main reasons were cost and "friendliness".&lt;/p&gt;

&lt;p&gt;While the price itself is one thing, by "friendliness" I mean both support and the pricing strategies down the line. We appreciate that FusionAuth doesn't have any "trap-like" pricing practices that could bite us when we happen to approach an arbitrary number of users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; What were some of the technical limitations you had that disqualified folks initially?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; It was mostly due to them not having react and/or python clients/SDKs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; I love the term "friendliness". Are there any other software packages you use that feel like they win in that category (other than FusionAuth and Contenda, of course)?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; On our frontend, we use Netlify and Pendo, and I think we could put them in this bucket as well. Generally smaller companies tend to care more about their customers, so it's not always the best idea to go with the biggest player out there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; How much time and money would you say FusionAuth has saved you?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás:&lt;/strong&gt; While I don't think I can come up with an exact number for either of those, I can confidently say: a lot. We could've spent months working on a complex system, and any FusionAuth employee could still say "Look what they need to mimic a fraction of our power" after that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[After building our own auth system in under a week] we definitely need something more robust, secure and user-friendly to get to the next magnitude of users.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; How do you run FusionAuth (kubernetes, standalone server, behind a proxy, etc)?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás and Grady:&lt;/strong&gt; We don't actually; you do!&lt;/p&gt;

&lt;p&gt;This was also an important point for us: engineering-hours are always the limiting factor, so if we can find a provider that also handles the infrastructure for us, that's fewer hours we waste on managing something we don't have to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; Any general feedback/areas to improve?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tamás:&lt;/strong&gt; I'd love it if more things were "easy" on the "easy &amp;lt;-&amp;gt; possible" scale. FusionAuth is extremely customizable, which is great, and makes a lot of things possible.&lt;/p&gt;

&lt;p&gt;But it'd be nice if the configuration interface would self-document itself more, and make a lot of the typical use-cases easy and simpler to do.&lt;/p&gt;

&lt;p&gt;Also, as a fan of strong typing: adding type hints to the python client library would be tremendous.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dan:&lt;/strong&gt; Thanks for your feedback!&lt;/p&gt;




&lt;p&gt;We love sharing community stories. You can check out &lt;a href="https://contenda.co/"&gt;Contenda's website&lt;/a&gt; if you'd like to learn more about them.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Adding single sign-on to a Next.js app using OIDC</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Wed, 26 Apr 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/fusionauth/adding-single-sign-on-to-a-nextjs-app-using-oidc-4g1b</link>
      <guid>https://forem.com/fusionauth/adding-single-sign-on-to-a-nextjs-app-using-oidc-4g1b</guid>
      <description>&lt;p&gt;Single sign-on (SSO) is a session and user authentication technique that permits a user to use one set of login credentials to authenticate with multiple apps. SSO works by establishing trust between a service provider, usually your application, and an identity provider, like FusionAuth.&lt;/p&gt;

&lt;p&gt;The trust is established through an out-of-band exchange of cryptographic credentials, such as a client secret or public key infrastructure (PKI) certificate. Once trust has been established between the service provider and the identity provider, SSO authentication can occur when a user wants to authenticate with the service provider. This will typically involve the service provider sending a token to the identity provider containing details about the user seeking authentication. The identity provider can then check if the user is already authenticated and ask them to authenticate if they are not. Once this is done, the identity provider can send a token back to the service provider, signifying that the user has been granted access.&lt;/p&gt;

&lt;p&gt;SSO helps reduce password fatigue by requiring users to only remember one password and username for all the applications managed through the SSO feature. This also reduces the number of support tickets created for your IT team when a user inevitably forgets their password. In addition, SSO minimizes the number of times you have to key-in security credentials, limiting exposure to security issues like keystroke probing and exposure. Security is also enhanced by SSO because you can implement additional functionality such as MFA or anomalous behavior detection at the identity provider without adding any complexity to your application.&lt;/p&gt;

&lt;p&gt;In this tutorial, you’ll learn how to design and implement SSO using &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;, a popular React-based framework for JavaScript and FusionAuth as the OIDC provider. Any other OIDC compatible authentication server should work as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing SSO in a Next.js web app
&lt;/h2&gt;

&lt;p&gt;As previously stated, in this tutorial, you’ll be shown how to implement SSO in a Next.js web app. The Next.js demo application is integrated with FusionAuth, an authentication and authorization platform, and &lt;a href="https://next-auth.js.org"&gt;NextAuth.js&lt;/a&gt;, a complete open-source authentication solution, in this article.&lt;/p&gt;

&lt;p&gt;Before you begin, you’ll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Linux machine. The step-by-step instructions in this article are based on a &lt;a href="https://www.centos.org"&gt;CentOS Linux machine&lt;/a&gt;. If you want to work on a different OS, check out this &lt;a href="https://dev.to/docs/v1/tech/5-minute-setup-guide"&gt;setup guide&lt;/a&gt; for more information.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/engine/"&gt;Docker Engine&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/"&gt;Docker Compose&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm"&gt;Node.js&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Experience with &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; framework and application development.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://git-scm.com/downloads"&gt;Git client tool&lt;/a&gt; &lt;em&gt;(optional)&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installing FusionAuth
&lt;/h2&gt;

&lt;p&gt;If you already have a FusionAuth Cloud instance, you can use that, but for the sake of simplicity, this tutorial will assume you are using a locally hosted instance. There are detailed setup guides in the &lt;a href="https://dev.to/docs/v1/tech/installation-guide/docker/"&gt;documentation&lt;/a&gt;, but the short version is that once you have Docker and Docker Compose set up and installed correctly, you can run the following command in a new directory to download and execute the necessary files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -o docker-compose.yml https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/docker-compose.yml curl -o .env https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/.env docker compose up 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Starting FusionAuth
&lt;/h3&gt;

&lt;p&gt;Once the FusionAuth service is started, open a browser and access &lt;a href="http://localhost:9011/"&gt;http://localhost:9011/&lt;/a&gt;, where you’ll be taken to the &lt;strong&gt;“FusionAuth Setup Wizard”&lt;/strong&gt; , as seen in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tt1PI8FM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/setup-wizard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tt1PI8FM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/setup-wizard.png" alt="FusionAuth setup wizard." width="800" height="758"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;FusionAuth setup wizard.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fill in the admin user account details, read and accept the License Agreement. When you are ready, click &lt;strong&gt;“Submit”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Learn more about the &lt;a href="https://fusionauth.io/docs/v1/tech/tutorials/setup-wizard"&gt;Setup Wizard here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After submitting, you’ll be taken to a login screen where you need to fill in the credentials you specified during the setup wizard and sign in to the FusionAuth administrative user interface. Later, you’ll use the same admin account for testing the SSO of the application.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configure FusionAuth
&lt;/h3&gt;

&lt;p&gt;Now you need to configure FusionAuth by creating an application and registering the user.&lt;/p&gt;

&lt;p&gt;This setup allows users in FusionAuth to sign in to the Next.js application automatically once they are authenticated by FusionAuth.&lt;/p&gt;
&lt;h3&gt;
  
  
  Set up the application
&lt;/h3&gt;

&lt;p&gt;To set up the application, navigate to the &lt;a href="http://localhost:9011/admin"&gt;FusionAuth admin UI&lt;/a&gt; and select “Applications” on the left-hand navigation bar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---xCOJ7JO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/set-up-the-application.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---xCOJ7JO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/set-up-the-application.png" alt="Set up the application." width="800" height="408"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;Set up the application.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then click on the “+” button on the top right of the “Applications” page and fill in the “Name” field with a name of your choosing (here, it’s &lt;strong&gt;“your_application”&lt;/strong&gt; ):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PB9hySqY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/name-the-application.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PB9hySqY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/name-the-application.png" alt="Name your application." width="800" height="610"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;Name your application.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can leave all the other fields empty because FusionAuth will choose a default value for those fields. Go ahead and save the application in order for the Client Id details to be generated.&lt;/p&gt;

&lt;p&gt;Access the “Application” page again and click on the “Edit Applications” icon (a little edit/notepad icon):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hsL3UYHT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/edit-application.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hsL3UYHT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/edit-application.png" alt="The list of applications. The edit icon is the blue pencil/notepad." width="800" height="408"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The list of applications. The edit icon is the blue pencil/notepad.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On the “Edit Application” page, click on the “OAuth” tab at the top. You’ll need to get some information from this page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--syubmWJP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/nextjs-single-sign-on/fusionauth-callback.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--syubmWJP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/nextjs-single-sign-on/fusionauth-callback.png" alt="The OAuth details tab." width="800" height="814"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The OAuth details tab.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record the generated “Client Id” and the “Client Secret” in a text file or to the clipboard. You’ll use these below.&lt;/li&gt;
&lt;li&gt;Add the value &lt;code&gt;http://localhost:3000/api/auth/callback/fusionauth&lt;/code&gt; to the “Authorized redirect URLs” field. This will be used by FusionAuth to redirect the user to your application page once the user is successfully authenticated.&lt;/li&gt;
&lt;li&gt;Scroll down and check the “Require registration” checkbox. This ensures that users who haven’t been registered for this application in FusionAuth aren’t able to access it when using the hosted login pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After filling in the details, click the “Save” icon.&lt;/p&gt;

&lt;p&gt;You aren’t limited to one application, by the way. If you have multiple applications, or even other applications like Zendesk or forum software, set them up here to enable SSO.&lt;/p&gt;
&lt;h3&gt;
  
  
  Register the user
&lt;/h3&gt;

&lt;p&gt;Next, register the user for the application. Navigate to the “Users” tab and find your user. Click on the black “Manage User” button under the “Action” heading in the list. You’ll end up at the details page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eWnYFsz6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/register-user.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eWnYFsz6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/register-user.png" alt="The user details screen, where you can register them." width="800" height="675"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The user details screen, where you can register them.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Click “Add registration” to register the user in &lt;code&gt;your_application&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZYLxJP4E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/add-registration.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZYLxJP4E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/add-registration.png" alt="Adding the user registration." width="800" height="635"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;Adding the user registration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If users of this application have unique configuration details, such as a username, timezone, or languages, which are different from the user’s defaults, you could configure them here.&lt;/p&gt;

&lt;p&gt;However, for this tutorial, you can click the blue “Save” button and accept the default values.&lt;/p&gt;
&lt;h3&gt;
  
  
  Kickstart
&lt;/h3&gt;

&lt;p&gt;Instead of manually setting up FusionAuth using the admin UI as you did above, you can use Kickstart. This tool allows you to get going quickly if you have a fresh installation of FusionAuth. Learn more about how to use &lt;a href="https://dev.to/docs/v1/tech/installation-guide/kickstart"&gt;Kickstart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s an example &lt;a href="https://github.com/FusionAuth/fusionauth-example-nextjs-single-sign-on/blob/main/kickstart/kickstart.json"&gt;Kickstart file&lt;/a&gt; which sets up FusionAuth for this tutorial.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create the Next.js application
&lt;/h2&gt;

&lt;p&gt;You have two options here. You can either clone a working example, or build the app from scratch.&lt;/p&gt;
&lt;h3&gt;
  
  
  Clone the demo repository
&lt;/h3&gt;

&lt;p&gt;If you want to run an already working application, you can clone the demo project from this &lt;a href="https://github.com/FusionAuth/fusionauth-example-nextjs-single-sign-on"&gt;GitHub repository&lt;/a&gt; using the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone git@github.com:FusionAuth/fusionauth-example-nextjs-single-sign-on.git 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since you’ve cloned a working rep, you don’t need to follow the next section. If you’d like to understand more about what was done in the demo application, feel free to read them.&lt;/p&gt;

&lt;p&gt;Either way, continue configuring the Environment variables to proceed with this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create your own Next.js application
&lt;/h2&gt;

&lt;p&gt;If you want to create your own application instead of using our demo project, you can create a new Next.js application by running the command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-next-app name-of-your-application 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If it is your first time creating a Next.js application, you’ll be prompted to install the &lt;code&gt;create-next-app&lt;/code&gt; package. Just type &lt;code&gt;y&lt;/code&gt; to accept it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;create-next-app&lt;/code&gt; wizard will ask you a few questions on how to set up your application. Answer them like shown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ Would you like to use TypeScript with this project? … No ✔ Would you like to use ESLint with this project? … Yes ✔ Would you like to use `src/` directory with this project? … Yes ? Would you like to use experimental `app/` directory with this project? … No ✔ What import alias would you like configured? … @/* 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the directory for the application you created and start up the application by running these commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd name-of-your-application npm run dev 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browse to &lt;a href="http://localhost:3000"&gt;localhost:3000&lt;/a&gt;. If the installation went successful, you should see the default welcome page from Next.js.&lt;/p&gt;

&lt;p&gt;Now that you have the application running, let’s implement the authentication process by using &lt;a href="https://next-auth.js.org"&gt;NextAuth.js&lt;/a&gt;, a complete open-source solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing NextAuth.js
&lt;/h3&gt;

&lt;p&gt;We recommend you look at the &lt;a href="https://next-auth.js.org/getting-started/example"&gt;NextAuth.js Getting Start guide&lt;/a&gt; for the most up-to-date instructions.&lt;/p&gt;

&lt;p&gt;First, install NextAuth.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install next-auth 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you need to create a file named exactly like &lt;code&gt;[...nextauth].js&lt;/code&gt; in &lt;code&gt;src/pages/api/auth&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, make the directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir src/pages/api/auth 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a file named &lt;code&gt;[...nextauth].js&lt;/code&gt; in that directory.&lt;/p&gt;

&lt;p&gt;Next, you’ll configure the &lt;a href="https://next-auth.js.org/providers/fusionauth"&gt;built-in support for FusionAuth&lt;/a&gt;. Doing so ensures every request to the &lt;code&gt;/api/auth/*&lt;/code&gt; path is handled by NextAuth.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import NextAuth from "next-auth" import FusionAuthProvider from "next-auth/providers/fusionauth" export const authOptions = { providers: [FusionAuthProvider({ issuer: process.env.FUSIONAUTH_ISSUER, clientId: process.env.FUSIONAUTH_CLIENT_ID, clientSecret: process.env.FUSIONAUTH_CLIENT_SECRET, wellKnown: `${process.env.FUSIONAUTH_URL}/.well-known/openid-configuration/${process.env.FUSIONAUTH_TENANT_ID}`, tenantId: process.env.FUSIONAUTH_TENANT_ID, // Only required if you're using multi-tenancy }),], } export default NextAuth(authOptions) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Exposing session state
&lt;/h3&gt;

&lt;p&gt;To allow components to check whether the current user is logged in, change &lt;code&gt;src/pages/_app.js&lt;/code&gt; to have your application rendered inside a &lt;code&gt;&amp;lt;SessionProvider&amp;gt;&lt;/code&gt; context, like shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '@/styles/globals.css' import { SessionProvider } from "next-auth/react" export default function App({ Component, pageProps: {session, ...pageProps}, }) { return ( &amp;lt;SessionProvider session={session}&amp;gt; &amp;lt;Component {...pageProps} /&amp;gt; &amp;lt;/SessionProvider&amp;gt; ); } 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will make the &lt;a href="https://next-auth.js.org/getting-started/client#usesession"&gt;&lt;code&gt;useSession()&lt;/code&gt;&lt;/a&gt; React Hook accessible to your entire application.&lt;/p&gt;

&lt;p&gt;Now, create a component that will either render a “Log in” or “Log out” button, depending on the session state, in a &lt;code&gt;src/components/login-button.jsx&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useSession, signIn, signOut } from "next-auth/react" export default function Component() { const { data: session } = useSession() if (session) { return ( &amp;lt;&amp;gt; Status: Logged in as {session.user.email} &amp;lt;br /&amp;gt; &amp;lt;button onClick={() =&amp;gt; signOut()}&amp;gt;Log out&amp;lt;/button&amp;gt; &amp;lt;/&amp;gt; ) } return ( &amp;lt;&amp;gt; Status: Not logged in &amp;lt;br /&amp;gt; &amp;lt;button onClick={() =&amp;gt; signIn()}&amp;gt;Log in&amp;lt;/button&amp;gt; &amp;lt;/&amp;gt; ) } 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you change your home component located at &lt;code&gt;src/pages/index.js&lt;/code&gt; to include the &lt;code&gt;&amp;lt;LoginButton /&amp;gt;&lt;/code&gt; component inside &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Head from 'next/head' import LoginButton from '@/components/login-button' export default function Home() { return ( &amp;lt;&amp;gt; &amp;lt;Head&amp;gt; &amp;lt;title&amp;gt;Next.js + FusionAuth&amp;lt;/title&amp;gt; &amp;lt;meta name="description" content="Sample app to demonstrate implementing authentication in a Next.js application with FusionAuth" /&amp;gt; &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1" /&amp;gt; &amp;lt;link rel="icon" href="/favicon.ico" /&amp;gt; &amp;lt;/Head&amp;gt; &amp;lt;main&amp;gt; &amp;lt;LoginButton /&amp;gt; &amp;lt;/main&amp;gt; &amp;lt;/&amp;gt; ) } 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Environment variables
&lt;/h2&gt;

&lt;p&gt;If you cloned the demo repository, you can copy &lt;code&gt;.env.local.dist&lt;/code&gt; to &lt;code&gt;.env.local&lt;/code&gt; and change the values there. If not, create a &lt;code&gt;.env.local&lt;/code&gt; file and fill in the details from your FusionAuth application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# example: my-website.com FUSIONAUTH_ISSUER=http://localhost:9011 FUSIONAUTH_CLIENT_ID=e9fdb985-9173-4e01-9d73-ac2d60d1dc8e FUSIONAUTH_CLIENT_SECRET=super-secret-secret-that-should-be-regenerated-for-production # Leave blank if you only have the default tenant FUSIONAUTH_TENANT_ID= # example: http://localhost:9011 FUSIONAUTH_URL=http://localhost:9011 # you can run `openssl rand -base64 32` NEXTAUTH_SECRET=random_value_please_change 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Start the HTTP server by running the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browse to &lt;a href="http://localhost:3000"&gt;localhost:3000&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VdekgrUp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/nextjs-single-sign-on/before-login.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VdekgrUp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/nextjs-single-sign-on/before-login.png" alt="The initial nextjs screen." width="800" height="182"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The initial nextjs screen.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Click the “Log in” button to be taken to a page with a “Sign in with FusionAuth” button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XwqbRcE0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/nextjs-single-sign-on/ask-for-login.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XwqbRcE0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/nextjs-single-sign-on/ask-for-login.png" alt="The sign-in button screen." width="800" height="814"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The sign-in button screen.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After clicking it, you should be redirected to your FusionAuth login screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OlO12KNt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/nextjs-single-sign-on/login-screen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OlO12KNt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/nextjs-single-sign-on/login-screen.png" alt="The FusionAuth sign-in screen." width="800" height="389"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The FusionAuth sign-in screen.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Enter the correct credentials created when you set up FusionAuth and submit the form. You’ll arrive back at your Next.js application home screen, with your email address displayed and a “Log out” button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k5H201Uv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/nextjs-single-sign-on/after-login.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k5H201Uv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/nextjs-single-sign-on/after-login.png" alt="The nextjs application after logging in." width="800" height="191"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The nextjs application after logging in.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;What’s next?&lt;/p&gt;

&lt;p&gt;You could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make FusionAuth &lt;a href="https://dev.to/docs/v1/tech/themes/"&gt;look like your nextjs application using themes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/learn/expert-advice/oauth/modern-guide-to-oauth/"&gt;Learn more about OAuth&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Build out a real nextjs application with a protected page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>sso</category>
      <category>nextjs</category>
      <category>oidc</category>
    </item>
    <item>
      <title>Save on your AWS bill with this one simple trick</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Mon, 03 Apr 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/fusionauth/save-on-your-aws-bill-with-this-one-simple-trick-2b83</link>
      <guid>https://forem.com/fusionauth/save-on-your-aws-bill-with-this-one-simple-trick-2b83</guid>
      <description>&lt;p&gt;With only a few clicks of the mouse, I saved 16% on my company’s AWS bill. Pretty sweet, eh?&lt;/p&gt;

&lt;p&gt;This article will show you why that matters, and how I did it.&lt;/p&gt;

&lt;h2&gt;
  
  
  FusionAuth Cloud lets you outsource operating your authentication service
&lt;/h2&gt;

&lt;p&gt;But first, let me set the scene. FusionAuth is &lt;a href="https://dev.to/download"&gt;downloadable software&lt;/a&gt; and running it is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -o docker-compose.yml https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/docker-compose.yml curl -o .env https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/.env docker-compose up 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have Docker installed, that’s all you have to do to get a FusionAuth instance up and running.&lt;/p&gt;

&lt;p&gt;After that, you need to integrate it with your application, typically using a standard OIDC library such as &lt;a href="https://www.passportjs.org/"&gt;Passport&lt;/a&gt; or &lt;a href="https://docs.spring.io/spring-security/reference/servlet/oauth2/client/index.html"&gt;Spring security&lt;/a&gt;. You need to &lt;a href="https://dev.to/docs/v1/tech/themes/"&gt;theme it&lt;/a&gt; to make it look like your application.&lt;/p&gt;

&lt;p&gt;But that’s it.&lt;/p&gt;

&lt;p&gt;With these steps, you get a full featured auth server, available on your laptop, for you to build against.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about production?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;docker-compose up&lt;/code&gt; is great for development.&lt;/p&gt;

&lt;p&gt;But auth is the front door to your application, and should be highly available in production. Some of the considerations when running a FusionAuth production deployment include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;securing the application and servers&lt;/li&gt;
&lt;li&gt;backups and restores&lt;/li&gt;
&lt;li&gt;upgrades and rollbacks&lt;/li&gt;
&lt;li&gt;responding to DDOS attacks&lt;/li&gt;
&lt;li&gt;monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FusionAuth Cloud, where the FusionAuth team operates FusionAuth, lets customers let us run FusionAuth for their authentication needs. It takes these tasks off your plate and puts them on that of the FusionAuth team.&lt;/p&gt;

&lt;p&gt;Now, there are many customers with the team, compliance needs, and desire to run FusionAuth themselves; we’re happy to support them in doing so with &lt;a href="https://dev.to/docs/v1/tech/installation-guide/cluster"&gt;documentation&lt;/a&gt; and customer support.&lt;/p&gt;

&lt;p&gt;But many users want to offload operational burdens and security concerns. In this aspect, FusionAuth Cloud is similar to a typical software as a service (SaaS) provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s so special about auth?
&lt;/h2&gt;

&lt;p&gt;But.&lt;/p&gt;

&lt;p&gt;An authentication service is not like any old SaaS application. Depending on your integration, it is embedded in your application. This is why Okta consistently has &lt;a href="https://investor.okta.com/static-files/8a461359-83f8-4332-9f41-eb7ee8c65c3b"&gt;negative churn&lt;/a&gt; on the order of 20%. Once you’re committed to an auth provider, it’s a biggish project to shift to another one.&lt;/p&gt;

&lt;p&gt;Worse, when your auth system breaks, your users don’t have access to your application. That’s bad.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--febGvPMD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fusionauth.io/assets/img/blogs/aws-savings-plans/bad-time.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--febGvPMD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fusionauth.io/assets/img/blogs/aws-savings-plans/bad-time.jpg" alt="It can be unpleasant when your users cannot log in." width="500" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because of these two attributes, with an auth provider you have requirements above and beyond those of a typical SaaS tool. FusionAuth Cloud offers the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It explicitly lets developers control the upgrade process; no required upgrades. You and your engineering team pick the version of software your application integrates with. You also choose how and when to upgrade. You can thoroughly test your integration when a new version comes out, rather than testing in prod or having new features foisted on you.&lt;/li&gt;
&lt;li&gt;Each customer is running on physically isolated single-tenant systems. Each FusionAuth Cloud deployment runs on separate virtual hardware, using Amazon’s best of breed managed services where appropriate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Isolation in multi-tenant SaaS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bgmg04-a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fusionauth.io/assets/img/blogs/aws-savings-plans/isolation.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bgmg04-a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fusionauth.io/assets/img/blogs/aws-savings-plans/isolation.jpg" alt="True isolation." width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s talk about isolation. When you offer a multi-tenant solution, with different customers using your application, there are multiple approaches to isolating their data and available functionality.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logical isolation&lt;/strong&gt; is implemented using a &lt;code&gt;tenant_id&lt;/code&gt; field on tables (or using proprietary database functionality such as row level security). When a request comes in, the tenant is identified and only that tenant’s data is examined by the application. This is the cheapest and easiest multi-tenant solution to run. You have one set of servers and one primary datastore in production. However, if there are any flaws in the code, everyone’s data may be accessible. You can scale this system, but you can’t scale any individual customer’s application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container level isolation&lt;/strong&gt; relies on containers and other software constructs such as Kubernetes namespaces. With this option, you have one set of servers underlying the containers in a cluster, though you can also outsource running it to a public cloud provider. Each customer gets their own set of containers running the code, as well as a separate database schema or server. This is more expensive, but provides a higher level of isolation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server isolation&lt;/strong&gt; is when you stand up separate virtual or physical machines. The application runs on each system. You can configure network layer protections such as firewalls, which increases the level of isolation. There’s also a lower chance of a noisy neighbor causing customers issues; the servers are separate. However, the operational cost is higher too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FusionAuth Cloud uses virtual machines and a shared-nothing architecture. Your data is isolated not within a database using foreign keys, but on physically separate machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  FusionAuth Cloud and EC2
&lt;/h2&gt;

&lt;p&gt;This architecture leads to a lot of VMs, as you might imagine. As FusionAuth Cloud grows, the FusionAuth team is responsible for more and more virtual machines.&lt;/p&gt;

&lt;p&gt;Which brings us back to the beginning. During a &lt;a href="https://dev.to/blog/2022/09/27/hackfests-fusionauth"&gt;hackfest&lt;/a&gt;, I took a look at reserved instances because I was interested in the cost savings opportunities. Most of FusionAuth’s AWS spend is with EC2 and RDS.&lt;/p&gt;

&lt;p&gt;Many of our customers are on month to month plans, and the minimum AWS reserved instance length is one year (unless you are using the reserved instances marketplace, but I’m going to ignore that because it makes things complicated).&lt;/p&gt;

&lt;p&gt;Our customers also choose one of 15+ AWS regions in which to deploy their FusionAuth servers. This allows them to pick the region that makes sense for their performance, compliance and data sovereignty needs, while still outsourcing operations to the FusionAuth team. Reserved instances are tied to an AWS region, making it an awkward choice.&lt;/p&gt;

&lt;p&gt;While we could have projected a baseline of region usage and VM lifetime and bought reserved instances based on that, this felt like a bigger project than was suitable for a hackfest.&lt;/p&gt;

&lt;p&gt;However, after more research, I discovered &lt;a href="https://aws.amazon.com/savingsplans/compute-pricing/"&gt;AWS Compute Savings Plans&lt;/a&gt;. There is an EC2 version that “appl[ies] to EC2 instance usage regardless of instance family, size, AZ, Region, OS or tenancy”.&lt;/p&gt;

&lt;p&gt;Perfect!&lt;/p&gt;

&lt;h2&gt;
  
  
  How much can you save?
&lt;/h2&gt;

&lt;p&gt;FusionAuth Cloud adds new customers regularly, and churn happens, especially when someone tries our &lt;a href="https://fusionauth.io/pricing?step=plan&amp;amp;hosting=basic-cloud"&gt;Starter Plan free trial&lt;/a&gt; and determines that FusionAuth isn’t the right fit.&lt;/p&gt;

&lt;p&gt;I wanted to avoid automating the purchase of these plans, which felt like over-optimization. I began with a manual solution.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://us-east-1.console.aws.amazon.com/cost-management/home#/savings-plans/overview"&gt;Savings plan overview&lt;/a&gt; has a “Recommendations” tab. You can play with options and arrive at a savings amount. This helps you determine if you have enough spend to make buying a plan worth your time. To begin with, I chose “Compute Savings Plans”, a “1-year” term, and “No upfront” as the purchase commitment. You get a deeper discount for prepayment or a longer commitment.&lt;/p&gt;

&lt;p&gt;Here’s a table of the discounts available to us at time of writing. Check out the Savings plans section yourself to see your discount options.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;No Upfront&lt;/th&gt;
&lt;th&gt;All Upfront&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 Year&lt;/td&gt;
&lt;td&gt;21%&lt;/td&gt;
&lt;td&gt;26%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 Year&lt;/td&gt;
&lt;td&gt;45%&lt;/td&gt;
&lt;td&gt;49%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I could have saved more money, but going with a year term and no upfront cost gave the right mix of savings and flexibility. A 21% discount on EC2 usage we already know will be running 100% of the time?&lt;/p&gt;

&lt;p&gt;Yes please.&lt;/p&gt;

&lt;h3&gt;
  
  
  Buying the Compute Savings plan
&lt;/h3&gt;

&lt;p&gt;Here’s the screen where I purchased the plan.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gtPycXpC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fusionauth.io/assets/img/blogs/aws-savings-plans/purchase-savings-plan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gtPycXpC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fusionauth.io/assets/img/blogs/aws-savings-plans/purchase-savings-plan.png" alt="The AWS cost explorer." width="880" height="895"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Compute Savings plans, you are buying hours of instances, but paying in dollars per hour. It’s a bit weird, but makes sense when viewed in the bigger picture. After all, it’s an AWS bill, so it can’t be &lt;em&gt;too&lt;/em&gt; straightforward.&lt;/p&gt;

&lt;p&gt;Here’s an example to make it a bit clearer.&lt;/p&gt;

&lt;p&gt;Let’s say you have 20 X1E Extra Large EC2 instances. These bad boys each cost $0.6160 per hour. I don’t know what you’re doing with that much RAM, but whatever.&lt;/p&gt;

&lt;p&gt;With this, you have $12.32/hour of spend (20*0.616). To save money, if you know you will run these servers for a year or so, use a Compute Savings plan and buy down a portion of the $12.32. You could buy $5 of that hourly spend for $3.95, for instance.&lt;/p&gt;

&lt;p&gt;After you buy the plan, you will be billed for that $3.95 whether you run the instances or don’t. Your total hourly bill is now $11.27, a savings of $1.05 or 8.5% across your entire spend.&lt;/p&gt;

&lt;p&gt;The bottom line is that AWS is thrilled when you commit to spending money with them, and is therefore willing to pay you to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a system
&lt;/h2&gt;

&lt;p&gt;Because of our growth, I wanted to avoid buying a savings plan as a one and done effort. I wanted a system that was lightweight, but would save us money as more customers came on board while preserving flexibility if we wanted to shift directions.&lt;/p&gt;

&lt;p&gt;Instead of buying the amount recommended by AWS, I bought 50% of it. In the future, we’ll do it again: purchase half of the recommended Compute Savings plan every few months.&lt;/p&gt;

&lt;p&gt;This will allow us to work towards having 100% of our usage covered by a savings plan. But we’ll never get there. Similar to &lt;a href="https://en.wikipedia.org/wiki/Zeno%27s_paradoxes"&gt;Achilles and the tortoise from Zeno’s paradox&lt;/a&gt; (image credit to wikipedia).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w3E27CDQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fusionauth.io/assets/img/blogs/aws-savings-plans/zeno-achilles-paradox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w3E27CDQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://fusionauth.io/assets/img/blogs/aws-savings-plans/zeno-achilles-paradox.png" alt="Achilles and the turtle, an example of Zeno's paradox. Image credit: wikipedia." width="440" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After determining that system would work, I documented it so I wouldn’t forget. This also lets others do this task if need be. Then I attached it to a recurring event in my calendar and promptly forgot about it. Every quarter or so, I get a reminder, and log into the AWS console and buy more discounted compute hours.&lt;/p&gt;

&lt;p&gt;This approach doesn’t maximize our savings, but it yields a useful amount (approximately 16% of our bill) and offers flexibility if our needs change. All at the cost of approximately 10 minutes every quarter.&lt;/p&gt;

&lt;p&gt;I’ve spent more time on this blog post than I have buying Compute Savings Plans over the past year. I’ll be honest, it’s fun to save thousands or tens of thousands of dollars with a single click and essentially zero downside.&lt;/p&gt;

&lt;p&gt;That’s the kind of clickops I can get behind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Downsides of AWS Compute Savings plans
&lt;/h2&gt;

&lt;p&gt;What about that downside? Well, if FusionAuth ever needed to leave AWS, we’d have to plan ahead a year or so or eat the cost of the committed spend. However, migrating off any cloud provider, especially when your system is the front door for millions of your customers’ customers, isn’t a task you undertake on a whim. That downside is acceptable.&lt;/p&gt;

&lt;p&gt;We’re also not saving as much as we could. If we signed up for three years instead of one, we’d save more but lose flexibility. If we optimized, researched reserved instances (especially for RDS servers), and automated plan purchases, we could save even more. But we’ve made a conscious decision to focus on growth. Spending too much time optimizing cloud spend doesn’t align with that.&lt;/p&gt;

&lt;p&gt;10 minutes a quarter, on the other hand, is pretty easy to find.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summing up
&lt;/h2&gt;

&lt;p&gt;If you are running significant numbers of EC2 instances, there’s very little risk in investigating and implementing cost savings plans.&lt;/p&gt;

&lt;p&gt;Happy saving!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Adding single sign-on to a Laravel app using Socialite and OIDC</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Mon, 13 Mar 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/fusionauth/adding-single-sign-on-to-a-laravel-app-using-socialite-and-oidc-5ag3</link>
      <guid>https://forem.com/fusionauth/adding-single-sign-on-to-a-laravel-app-using-socialite-and-oidc-5ag3</guid>
      <description>&lt;p&gt;Single sign-on (SSO) is a session and user authentication technique that permits a user to use one set of login credentials to authenticate with multiple apps. SSO works by establishing trust between a service provider, usually your application, and an identity provider, like FusionAuth.&lt;/p&gt;

&lt;p&gt;The trust is established through an out-of-band exchange of cryptographic credentials, such as a client secret or public key infrastructure (PKI) certificate. Once trust has been established between the service provider and the identity provider, SSO authentication can occur when a user wants to authenticate with the service provider. This will typically involve the service provider sending a token to the identity provider containing details about the user seeking authentication. The identity provider can then check if the user is already authenticated and ask them to authenticate if they are not. Once this is done, the identity provider can send a token back to the service provider, signifying that the user has been granted access.&lt;/p&gt;

&lt;p&gt;SSO helps reduce password fatigue by requiring users to only remember one password and username for all the applications managed through the SSO feature. This also reduces the number of support tickets created for your IT team when a user inevitably forgets their password. In addition, SSO minimizes the number of times you have to key-in security credentials, limiting exposure to security issues like keystroke probing and exposure. Security is also enhanced by SSO because you can implement additional functionality such as MFA or anomalous behavior detection at the identity provider without adding any complexity to your application.&lt;/p&gt;

&lt;p&gt;In this tutorial, you’ll learn how to design and implement SSO using &lt;a href="https://laravel.com"&gt;Laravel&lt;/a&gt;, a popular PHP-based web framework and FusionAuth as the OIDC provider. Any other OIDC compatible authentication server should work as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing SSO in a Laravel web app
&lt;/h2&gt;

&lt;p&gt;As previously stated, in this tutorial, you’ll be shown how to implement SSO in a Laravel web app. The Laravel demo application is integrated with FusionAuth, an authentication and authorization platform, and &lt;a href="https://laravel.com/docs/9.x/socialite"&gt;Laravel Socialite&lt;/a&gt;, its official solution to authenticate users with OAuth providers.&lt;/p&gt;

&lt;p&gt;Before you begin, you’ll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Linux machine. The step-by-step instructions in this article are based on a &lt;a href="https://www.centos.org"&gt;CentOS Linux machine&lt;/a&gt;. If you want to work on a different OS, check out this &lt;a href="https://dev.to/docs/v1/tech/5-minute-setup-guide"&gt;setup guide&lt;/a&gt; for more information.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/engine/"&gt;Docker Engine&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/"&gt;Docker Compose&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Experience with a Laravel framework and application development.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://git-scm.com/downloads"&gt;Git client tool&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Clone the GitHub repository &lt;em&gt;(optional)&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;If you don’t want to keep copying and pasting code from this article, you can clone this &lt;a href="https://github.com/FusionAuth/fusionauth-example-laravel-single-sign-on"&gt;GitHub repository&lt;/a&gt; using the command below. You’d still have to configure the environment variables though.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/FusionAuth/fusionauth-example-laravel-single-sign-on 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing FusionAuth
&lt;/h2&gt;

&lt;p&gt;If you already have a FusionAuth Cloud instance, you can use that, but for the sake of simplicity, this tutorial will assume you are using a locally hosted instance. There are detailed setup guides in the &lt;a href="https://dev.to/docs/v1/tech/installation-guide/docker/"&gt;documentation&lt;/a&gt;, but the short version is that once you have Docker and Docker Compose set up and installed correctly, you can run the following command in a new directory to download and execute the necessary files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -o docker-compose.yml https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/docker-compose.yml $ curl -o .env https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/.env $ docker compose up 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Starting FusionAuth
&lt;/h3&gt;

&lt;p&gt;Once the FusionAuth service is started, open a browser and access &lt;a href="http://localhost:9011/"&gt;http://localhost:9011/&lt;/a&gt;, where you’ll be taken to the &lt;strong&gt;“FusionAuth Setup Wizard”&lt;/strong&gt; , as seen in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tt1PI8FM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/setup-wizard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tt1PI8FM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/setup-wizard.png" alt="FusionAuth setup wizard." width="800" height="758"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;FusionAuth setup wizard.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fill in the admin user account details, read and accept the License Agreement. When you are ready, click &lt;strong&gt;“Submit”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Learn more about the &lt;a href="https://fusionauth.io/docs/v1/tech/tutorials/setup-wizard"&gt;Setup Wizard here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After submitting, you’ll be taken to a login screen where you need to fill in the credentials you specified during the setup wizard and sign in to the FusionAuth administrative user interface. Later, you’ll use the same admin account for testing the SSO of the application.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configure FusionAuth
&lt;/h3&gt;

&lt;p&gt;Now you need to configure FusionAuth.&lt;/p&gt;

&lt;p&gt;First, you’ll configure the application, then register the user for the application.&lt;/p&gt;

&lt;p&gt;This setup allows users in FusionAuth to sign in to the Laravel application automatically once they are authenticated by FusionAuth.&lt;/p&gt;
&lt;h3&gt;
  
  
  Set up the application
&lt;/h3&gt;

&lt;p&gt;To set up the application, navigate to the &lt;a href="http://localhost:9011/admin"&gt;FusionAuth admin UI&lt;/a&gt; and select “Applications” on the left-hand navigation bar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---xCOJ7JO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/set-up-the-application.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---xCOJ7JO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/set-up-the-application.png" alt="Set up the application." width="800" height="408"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;Set up the application.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then click on the “+” button on the top right of the “Applications” page and fill in the “Name” field with a name of your choosing (here, it’s &lt;strong&gt;“your_application”&lt;/strong&gt; ):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PB9hySqY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/name-the-application.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PB9hySqY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/name-the-application.png" alt="Name your application." width="800" height="610"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;Name your application.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can leave all the other fields empty because FusionAuth will choose a default value for those fields. Go ahead and save the application in order for the Client Id details to be generated.&lt;/p&gt;

&lt;p&gt;Access the “Application” page again and click on the “Edit Applications” icon (a little edit/notepad icon):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hsL3UYHT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/edit-application.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hsL3UYHT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/edit-application.png" alt="The list of applications. The edit icon is the blue pencil/notepad." width="800" height="408"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The list of applications. The edit icon is the blue pencil/notepad.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On the “Edit Application” page, click on the “OAuth” tab at the top. You’ll need to get some information from this page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q2TZFwZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/laravel-single-sign-on/oauth-details.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q2TZFwZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/laravel-single-sign-on/oauth-details.png" alt="The OAuth details tab." width="800" height="813"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The OAuth details tab.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record the generated “Client Id” and the “Client Secret” in a text file or to the clipboard. You’ll use these below.&lt;/li&gt;
&lt;li&gt;Add the value &lt;code&gt;http://localhost/auth/callback&lt;/code&gt; to the “Authorized redirect URLs” field. This will be used by FusionAuth to redirect the user to your application page once the user is successfully authenticated.&lt;/li&gt;
&lt;li&gt;Scroll down and check the “Require registration” checkbox. This ensures that users who haven’t been registered for this application in FusionAuth aren’t able to access it when using the hosted login pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After filling in the details, click the “Save” icon.&lt;/p&gt;

&lt;p&gt;You aren’t limited to one application, by the way. If you have multiple applications, or even other applications like Zendesk or forum software, set them up here to enable SSO.&lt;/p&gt;
&lt;h3&gt;
  
  
  Register the user
&lt;/h3&gt;

&lt;p&gt;Next, register the user for the application. Navigate to the “Users” tab and find your user. Click on the black “Manage User” button under the “Action” heading in the list. You’ll end up at the details page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eWnYFsz6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/register-user.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eWnYFsz6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/register-user.png" alt="The user details screen, where you can register them." width="800" height="675"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The user details screen, where you can register them.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Click “Add registration” to register the user in &lt;code&gt;your_application&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZYLxJP4E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/add-registration.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZYLxJP4E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/includes/fusionauth/add-registration.png" alt="Adding the user registration." width="800" height="635"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;Adding the user registration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If users of this application have unique configuration details, such as a username, timezone, or languages, which are different from the user’s defaults, you could configure them here.&lt;/p&gt;

&lt;p&gt;However, for this tutorial, you can click the blue “Save” button and accept the default values.&lt;/p&gt;
&lt;h3&gt;
  
  
  Kickstart
&lt;/h3&gt;

&lt;p&gt;Instead of manually setting up FusionAuth using the admin UI as you did above, you can use Kickstart. This tool allows you to get going quickly if you have a fresh installation of FusionAuth. Learn more about how to use &lt;a href="https://fusionauth.io/docs/v1/tech/installation-guide/kickstart"&gt;Kickstart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s an example &lt;a href="https://github.com/FusionAuth/fusionauth-example-laravel-single-sign-on/blob/main/fusionauth/kickstart/kickstart.json"&gt;Kickstart file&lt;/a&gt; which sets up FusionAuth for this tutorial.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing and configuring Laravel
&lt;/h2&gt;

&lt;p&gt;If you already have a running Laravel application, please skip this step.&lt;/p&gt;

&lt;p&gt;There are several ways of installing Laravel, but we recommend using it inside a Docker container via Laravel Sail. To do so, you can execute the command below to automatically download and install every package needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s https://laravel.build/my-app | bash 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can change the name of your application from &lt;code&gt;my-app&lt;/code&gt; from the URL above to anything you like, as long as it only contains alphanumeric characters, dashes and underscores.&lt;/p&gt;

&lt;p&gt;After everything is finished, you can enter the application directory and start it via Sail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd my-app $ ./vendor/bin/sail up -d 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command &lt;code&gt;./vendor/bin/sail&lt;/code&gt; has a lot of built-in tools to handle the containerized application from your host machine and it forwards every unknown argument to &lt;code&gt;docker compose&lt;/code&gt;. So, the command above would actually just call &lt;code&gt;docker compose up -d&lt;/code&gt; with the necessary context and environment to run it successfully.&lt;/p&gt;

&lt;p&gt;To test whether the Laravel application is up and running as expected, open a browser and access &lt;a href="http://localhost"&gt;http://localhost&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uTa8HQpJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/laravel-single-sign-on/laravel-application.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uTa8HQpJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/laravel-single-sign-on/laravel-application.png" alt="The default Laravel application." width="800" height="262"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The default Laravel application.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Laravel Socialite
&lt;/h3&gt;

&lt;p&gt;According to its own docs, &lt;a href="https://laravel.com/docs/9.x/socialite"&gt;Laravel Socialite&lt;/a&gt; is a &lt;em&gt;“a simple, convenient way to authenticate with OAuth providers”&lt;/em&gt;, meaning that is a very straightforward way of calling a remote server in order to authenticate a user. And that is exactly what FusionAuth is all about!&lt;/p&gt;

&lt;p&gt;Officially, it ships with support for Facebook, Twitter, LinkedIn, Google, GitHub, GitLab and Bitbucket, but there are several community projects that add other providers, and they are grouped in a project called &lt;a href="https://socialiteproviders.com/"&gt;Socialite Providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to the open-source community, there is &lt;a href="https://github.com/SocialiteProviders/FusionAuth"&gt;this package to use FusionAuth as a provider&lt;/a&gt;, which we’ll now use to show you how it’s done!&lt;/p&gt;
&lt;h3&gt;
  
  
  Installing the FusionAuth provider
&lt;/h3&gt;

&lt;p&gt;The first thing you need to do is add the &lt;a href="https://github.com/SocialiteProviders/FusionAuth"&gt;FusionAuth provider&lt;/a&gt; as a dependency to your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./vendor/bin/sail composer require socialiteproviders/fusionauth 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When running the command above, &lt;a href="https://getcomposer.org"&gt;composer&lt;/a&gt; will automatically install the necessary &lt;code&gt;laravel/socialite&lt;/code&gt; package&lt;/p&gt;

&lt;p&gt;Alter the service provider responsible for listening to events at &lt;code&gt;app/Providers/EventServiceProvider.php&lt;/code&gt; to tell Laravel that it should call &lt;code&gt;FusionAuthExtendSocialite&lt;/code&gt; class when there is an event for Socialite. To do this, change its &lt;code&gt;$listen&lt;/code&gt; property to add the following entry to the array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected $listen = [\SocialiteProviders\Manager\SocialiteWasCalled::class =&amp;gt; [ \SocialiteProviders\FusionAuth\FusionAuthExtendSocialite::class . '@handle',], ]; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you already have something in the &lt;code&gt;$listen&lt;/code&gt; array (like &lt;code&gt;Registered::class =&amp;gt; [...]&lt;/code&gt;), please keep it and add a new entry to the array&lt;/p&gt;

&lt;p&gt;Now, configure your application with the necessary settings to interact with the FusionAuth service, by adding a new entry to the array located at &lt;code&gt;config/services.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'fusionauth' =&amp;gt; ['client_id' =&amp;gt; env('FUSIONAUTH_CLIENT_ID'), 'client_secret' =&amp;gt; env('FUSIONAUTH_CLIENT_SECRET'), 'redirect' =&amp;gt; env('FUSIONAUTH_REDIRECT_URI'), 'base_url' =&amp;gt; env('FUSIONAUTH_BASE_URL'), 'tenant_id' =&amp;gt; env('FUSIONAUTH_TENANT_ID'),], 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring environment variables
&lt;/h3&gt;

&lt;p&gt;Instead of putting the values directly there, you should use environment variables, which is a good practice from both maintenance and security standpoints, as described in the &lt;a href="https://12factor.net/config"&gt;12 Factor App&lt;/a&gt; methodology and in several others. Being so, alter your &lt;code&gt;.env&lt;/code&gt; file to include those entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Paste both client Id and secret for your application FUSIONAUTH_CLIENT_ID=&amp;lt;APP CLIENT ID FROM FUSIONAUTH&amp;gt; FUSIONAUTH_CLIENT_SECRET=&amp;lt;APP CLIENT SECRET FROM FUSIONAUTH&amp;gt; # Specify a tenant Id or leave this blank for the default tenant FUSIONAUTH_TENANT_ID=&amp;lt;ID FOR YOUR TENANT&amp;gt; # Replace http://localhost:9011 with the address where the FusionAuth application is running FUSIONAUTH_BASE_URL=http://localhost:9011 # Replace http://localhost with the address for your PHP application, if necessary # We'll create the route for `/auth/callback` later## FUSIONAUTH_REDIRECT_URI=http://localhost/auth/callback 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating new fields in the User model
&lt;/h3&gt;

&lt;p&gt;Migrations are the code-based version control for your database. Instead of having to share SQL queries with your coworkers so they can create a table or add a column everytime there is a change in the database, you can write code to programmatically do this when running a command.&lt;/p&gt;

&lt;p&gt;In Laravel, to create a new migration you just need to run the &lt;code&gt;artisan make:migration&lt;/code&gt; and inform a meaningful name for it. In the next line, we’ll create a migration called &lt;code&gt;add_fusionauth_fields_user_table&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./vendor/bin/sail artisan make:migration add_fusionauth_fields_user_table 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, go to your &lt;code&gt;database/migrations&lt;/code&gt; folder, where you’ll find a file there with the name you created and prefixed by today’s date (e.g &lt;code&gt;2023_02_03_123000_add_fusionauth_fields_user_table.php&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Edit that file to add three new columns to the &lt;code&gt;users&lt;/code&gt; table: &lt;code&gt;fusionauth_id&lt;/code&gt;, &lt;code&gt;fusionauth_access_token&lt;/code&gt; and &lt;code&gt;fusionauth_refresh_token&lt;/code&gt; and allow &lt;code&gt;NULL&lt;/code&gt; values in the &lt;code&gt;password&lt;/code&gt; column. You’d want to save the access token in your database to make requests to the FusionAuth API or other APIs that accept FusionAuth access tokens on behalf of the user. The refresh token is used to exchange an expired access token with a new one (but you’d still have to implement this yourself, as Socialite still doesn’t do it). This way, users don’t need to log in again to your FusionAuth instance as long as the refresh token is still valid.&lt;/p&gt;

&lt;p&gt;If you cloned our &lt;a href="https://github.com/FusionAuth/fusionauth-example-laravel-single-sign-on"&gt;GitHub repository&lt;/a&gt;, you can just copy the contents from &lt;a href="https://github.com/FusionAuth/fusionauth-example-laravel-single-sign-on/blob/main/laravel/migration.php"&gt;laravel/migration.php&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /*** When running this migration, we want to create three new columns and change the password so it can be NULL * * @return void */ public function up() { Schema::table('users', function (Blueprint $table) { $table-&amp;gt;string('fusionauth_id', 36)-&amp;gt;unique(); $table-&amp;gt;text('fusionauth_access_token'); $table-&amp;gt;text('fusionauth_refresh_token')-&amp;gt;nullable(); $table-&amp;gt;string('password')-&amp;gt;nullable()-&amp;gt;change(); }); } /** * When rolling back this migration, we want to drop the added columns and revert the password to NOT NULL again * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table-&amp;gt;dropColumn('fusionauth_id'); $table-&amp;gt;dropColumn('fusionauth_access_token'); $table-&amp;gt;dropColumn('fusionauth_refresh_token'); $table-&amp;gt;string('password')-&amp;gt;nullable(false)-&amp;gt;change(); }); } }; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to add &lt;code&gt;doctrine/dbal&lt;/code&gt; as a dependency, which is a package responsible for changing table and column definitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./vendor/bin/sail composer require doctrine/dbal 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the migration to add those new columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./vendor/bin/sail php artisan migrate INFO Running migrations. 2023_02_03_123000_add_fusionauth_fields_user_table ........................... 51ms DONE 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you need to change the User model so it can become aware of these new columns. Modify &lt;code&gt;app/Models/User.php&lt;/code&gt; and add them to both &lt;code&gt;$fillable&lt;/code&gt; and &lt;code&gt;$hidden&lt;/code&gt; properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*** The attributes that are mass assignable. * * @var array&amp;lt;int, string&amp;gt; */ protected $fillable = ['name', 'email', 'password', 'fusionauth_id', // Add this column here 'fusionauth_access_token', // Add this column here 'fusionauth_refresh_token', // Add this column here]; /** * The attributes that should be hidden for serialization. * * @var array&amp;lt;int, string&amp;gt; */ protected $hidden = ['password', 'remember_token', 'fusionauth_id', // Add this column here 'fusionauth_access_token', // Add this column here 'fusionauth_refresh_token', // Add this column here]; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding routes
&lt;/h3&gt;

&lt;p&gt;Now that your &lt;code&gt;Users&lt;/code&gt; model and table have more details about the integration, you should define the necessary routes to redirect the user to the FusionAuth login and the page that the user will be redirected to after completing the process and obtaining an access token there. Add them to the &lt;code&gt;routes/web.php&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;If you cloned our &lt;a href="https://github.com/FusionAuth/fusionauth-example-laravel-single-sign-on"&gt;GitHub repository&lt;/a&gt;, you can just copy the contents from &lt;a href="https://github.com/FusionAuth/fusionauth-example-laravel-single-sign-on/blob/main/laravel/routes.php"&gt;laravel/routes.php&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php use App\Models\User; use Illuminate\Support\Facades\Auth; use Laravel\Socialite\Facades\Socialite; /*** Responsible for redirecting the user to the FusionAuth login page */ Route::get('/auth/redirect', function () { return Socialite::driver('fusionauth')-&amp;gt;redirect(); })-&amp;gt;name('login'); /** * This is the address that we configured in our .env file which the user will be redirected to after completing the * login process */ Route::get('/auth/callback', function () { /** @var \SocialiteProviders\Manager\OAuth2\User $user */ $user = Socialite::driver('fusionauth')-&amp;gt;user(); // Let's create a new entry in our users table (or update if it already exists) with some information from the user $user = User::updateOrCreate(['fusionauth_id' =&amp;gt; $user-&amp;gt;id,], ['name' =&amp;gt; $user-&amp;gt;name, 'email' =&amp;gt; $user-&amp;gt;email, 'fusionauth_access_token' =&amp;gt; $user-&amp;gt;token, 'fusionauth_refresh_token' =&amp;gt; $user-&amp;gt;refreshToken,]); // Logging the user in Auth::login($user); // Here, you should redirect to your app's authenticated pages (e.g. the user dashboard) return redirect('/'); }); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Changing the view
&lt;/h3&gt;

&lt;p&gt;To better see what is going on after logging in, you can change the view for the home page to actually display the name or the current user. You can do this by opening &lt;code&gt;resources/views/welcome.blade.php&lt;/code&gt; and adding a new line after “Home”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Before: @auth &amp;lt;a href="{{ url('/home') }}" class="font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500"&amp;gt;Home&amp;lt;/a&amp;gt; @else // ... // After: @auth &amp;lt;a href="{{ url('/home') }}" class="font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500"&amp;gt;Home&amp;lt;/a&amp;gt; &amp;lt;span class="ml-4 text-gray-600 dark:text-gray-400"&amp;gt;Welcome, &amp;lt;span class="font-semibold"&amp;gt;{{ Auth::user()-&amp;gt;name }}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; @else // ... 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;It is finally time to open your application in the browser and test everything! Navigate to &lt;a href="http://localhost"&gt;http://localhost&lt;/a&gt; and click the “Log in” link in the upper right corner of that page to be taken to FusionAuth’s login screen. Provide the credentials for the user you created earlier and submit the form: you should be redirected back to that “Welcome” page but instead of seeing the same “Log in” link, you should now see both “Home” and your name there!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uB7eMRtc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/laravel-single-sign-on/laravel-application-logged-in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uB7eMRtc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fusionauth.io/assets/img/blogs/laravel-single-sign-on/laravel-application-logged-in.png" alt="The default Laravel application while being logged in." width="800" height="262"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;The default Laravel application while being logged in.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now you have a working Laravel application that has been authenticated by the user created in FusionAuth. On successful sign-in to the integrated OAuth and identity provider, the user is automatically signed in in the Laravel application!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SSO-based authentication eases the process of signing in to applications, and it’s a popular choice for many organizations. In this article, you learned about SSO and authentication platforms like &lt;a href="https://dev.to/"&gt;FusionAuth&lt;/a&gt;, as well as how you can integrate a Laravel application with FusionAuth to successfully enable SSO-based authentication.&lt;/p&gt;

&lt;p&gt;Get your hands dirty by &lt;a href="https://dev.to/download"&gt;downloading FusionAuth for free&lt;/a&gt; and set up SSO for your Laravel app today.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using and Managing Consents in an Express App</title>
      <dc:creator>FusionAuth</dc:creator>
      <pubDate>Mon, 20 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/fusionauth/using-and-managing-consents-in-an-express-app-40hg</link>
      <guid>https://forem.com/fusionauth/using-and-managing-consents-in-an-express-app-40hg</guid>
      <description>&lt;p&gt;In this tutorial, we’ll build a basic Node.js and &lt;a href="http://expressjs.com" rel="noopener noreferrer"&gt;Express&lt;/a&gt; web application with advanced user registration and authentication via FusionAuth. We’ll create a custom registration form, along with custom consent options for marketing preferences, and set up self-service options for users to update their profile and consent permissions.&lt;/p&gt;

&lt;p&gt;The application itself is very simple: it will let users sign up via FusionAuth, allow them to set their permissions for marketing consent, and allow them to update their profile and consents at any time. With these basics in place, you’ll see how FusionAuth works and how it can extend the application to do whatever you need. You can also &lt;a href="https://github.com/fusionauth/fusionauth-example-express-consents" rel="noopener noreferrer"&gt;skip ahead and view the code&lt;/a&gt;, although much of the application is defined in FusionAuth as detailed in this guide.&lt;/p&gt;

&lt;p&gt;The profile and custom registration forms described below are part of our paid plans. Please see the &lt;a href="https://dev.to/pricing"&gt;pricing page&lt;/a&gt; for more information about paid plans. Consents, however, are community plan features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;We’ll explain nearly everything that we use, but we expect you to have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic Node.js knowledge and a Node.js environment set up.&lt;/li&gt;
&lt;li&gt;Preferably basic &lt;a href="http://expressjs.com" rel="noopener noreferrer"&gt;Express&lt;/a&gt; knowledge (or knowledge of a similar web framework or the middleware concept).&lt;/li&gt;
&lt;li&gt;Docker and Docker Compose set up as we’ll set up FusionAuth using these.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’ll also help if you know the basics of OAuth or authentication in general.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why FusionAuth instead of plain Passport?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.passportjs.org" rel="noopener noreferrer"&gt;Passport&lt;/a&gt; is one of the commonly used authentication systems in Express apps. It is very powerful and allows you to hook into social providers, OpenID and OAuth providers, or use a local authentication strategy. This sounds like everything you’ll ever need, but there are still a few missing pieces. For example, you still need to construct your own login page and other account functionality such as changing passwords, forgotten password resets, 2FA, email verification, account protection, and more. Setting up custom web app authentication is always more complicated than it seems.&lt;/p&gt;

&lt;p&gt;You would also need to implement functionality to allow users to update their profile information. Part of users’ profile and account data is inevitably consent permissions. Most apps will need to gather users’ consent for activities such as sending marketing updates or sharing users’ data with affiliates and other third parties. This would normally require coding, storing, and maintenance with custom solutions. However, since it is an integral part of user identity, FusionAuth has consent management built in.&lt;/p&gt;

&lt;p&gt;The great news is that combining Passport with FusionAuth makes a complete system that takes care of all aspects of authentication and identity. It also means that much of your app’s authentication capability can be configured through FusionAuth rather than writing code and modifying your app. For example, you can easily add registration form fields whenever you need to, without changing code or redeploying your app.&lt;/p&gt;

&lt;p&gt;With this setup, authentication, identity, and consent concerns are taken care of entirely by FusionAuth.&lt;/p&gt;

&lt;p&gt;The image below shows how this works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Farchitecture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Farchitecture.png" alt="Important private data goes in FusionAuth. Everything else in Node-Express. User consent information also stored and managed by FusionAuth" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your application logic and all public information can be handled by Express. Anything sensitive, such as personally identifiable information (PII), passwords, and consent permissions, is handled by FusionAuth.&lt;/p&gt;

&lt;p&gt;This allows you to focus your security efforts on the FusionAuth installation. It also means that if you create more applications, they can piggyback on your centralized authentication and you don’t need to re-implement authentication each time. You can also create a multi-tenant configuration, allowing you to easily have logically separate environments for different clients.&lt;/p&gt;

&lt;p&gt;Also, any integrations you set up with other providers (for example, Twitter, Google, Apple sign-in) can be done once instead of per application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing and configuring FusionAuth with Docker Compose
&lt;/h2&gt;

&lt;p&gt;There are &lt;a href="https://dev.to/docs/v1/tech/installation-guide/fusionauth-app"&gt;various ways&lt;/a&gt; to install FusionAuth depending on your system, but the easiest way is to use Docker and Docker Compose. Instructions are &lt;a href="https://dev.to/docs/v1/tech/installation-guide/docker"&gt;here&lt;/a&gt;. Currently, if you have Docker installed, to install and run FusionAuth you would run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -o docker-compose.yml https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/docker-compose.yml https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/docker-compose.override.yml curl -o .env https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/.env docker-compose up 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this uses a public &lt;code&gt;.env&lt;/code&gt; file containing hard-coded database passwords and is not suitable for production use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring FusionAuth
&lt;/h3&gt;

&lt;p&gt;FusionAuth should now be running and reachable at &lt;code&gt;http://localhost:9011&lt;/code&gt; if you’ve installed it locally. The first time you visit, you’ll be prompted to set up an admin user and password. Once you’ve done this, you’ll be prompted to complete three more set-up steps, as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Ffusionauth-setup1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Ffusionauth-setup1.png" alt="FusionAuth prompts us with the set-up steps that we need to complete." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll skip &lt;strong&gt;step 3&lt;/strong&gt; in this tutorial, but sending emails (to verify email addresses and do password resets) is a vital part of FusionAuth running in production, so you’ll want to do that when you go live.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating an application
&lt;/h3&gt;

&lt;p&gt;Click “Setup” under “Missing Application” and call your new app “Consents-App”, or another name of your choice. It’ll get a Client Id and Client Secret automatically - save these, as we’ll use them in the code. Later, we’ll set up a Node.js and Express application that will run on &lt;code&gt;http://localhost:3000&lt;/code&gt;, so configure the Authorized URLs accordingly. You should add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http://localhost:3000/auth/callback&lt;/code&gt; to the Authorized redirect URLs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http://localhost:3000/&lt;/code&gt; to the Authorized request origin URL.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http://localhost:3000/&lt;/code&gt; to the Logout URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Ffusionauth-urlconf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Ffusionauth-urlconf.png" alt="Configuring the application URLs in FusionAuth." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the “Save” button at the top right for your changes to take effect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a FusionAuth API key
&lt;/h2&gt;

&lt;p&gt;Once the user has logged in via the FusionAuth application, we can retrieve their FusionAuth profile and consent permissions using the &lt;a href="https://www.npmjs.com/package/@fusionauth/typescript-client" rel="noopener noreferrer"&gt;FusionAuth Typescript module&lt;/a&gt;, provided with an API key.&lt;/p&gt;

&lt;p&gt;Navigate to “Settings”, then “API Keys”, and add a key. Add a name for the key and take note of the generated key value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fgettingapikey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fgettingapikey.png" alt="Getting the API key from FusionAuth." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For extra security, you can restrict the permissions for the key. For our app, we only need to enable the get actions for &lt;code&gt;/api/user/&lt;/code&gt; and &lt;code&gt;/api/user/consent&lt;/code&gt; that will let the key get basic user information, as well as any consents. If you leave the key with no explicitly assigned permissions, it will be an all-powerful key that can control all aspects of your FusionAuth app. You should avoid doing this!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fgettingapikey-limited-scope.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fgettingapikey-limited-scope.png" alt="Limiting the scope of the created API key." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the custom consents
&lt;/h2&gt;

&lt;p&gt;For our app, we want users to be able to opt-in, or &lt;em&gt;consent&lt;/em&gt;, to different marketing channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physical mail&lt;/li&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;Phone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll need to get their permission for each of these options. FusionAuth manages all this under the &lt;a href="https://fusionauth.io/docs/v1/tech/apis/consents" rel="noopener noreferrer"&gt;Consents&lt;/a&gt; concept. We can create custom consents for our app.&lt;/p&gt;

&lt;p&gt;In the left-hand pane, navigate to Settings &amp;gt; Consents. Click the green “+” button to create a new consent. Name the new consent “Email Marketing” and click the “Save” icon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fcreate-consent.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fcreate-consent.png" alt="Create a new consent" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat this to create two more consents: “Phone Marketing” and “Physical Mail Marketing”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating custom form fields
&lt;/h2&gt;

&lt;p&gt;During user registration for our app, we want users to be able to set their important profile information as well as their consent permissions. For this, we can create custom form fields that we will be able to use in a custom registration form. FusionAuth also has many built-in form fields, which we will also use.&lt;/p&gt;

&lt;p&gt;The custom fields we’ll create are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physical Address.&lt;/li&gt;
&lt;li&gt;Form Input for Physical Mail Marketing Consent.&lt;/li&gt;
&lt;li&gt;Form Input for Email Marketing Consent.&lt;/li&gt;
&lt;li&gt;Form Input for Phone Marketing Consent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the left-hand pane, navigate to Customizations &amp;gt; Form Fields. Click the green “+” button to create a new form field.&lt;/p&gt;

&lt;p&gt;First, we’ll add the marketing consent fields, starting with the physical mail marketing consent. Choose “Self Consent” as the field type, and set the “Name” to “Physical Mail Marketing Consent”. Select “Physical Mail Marketing” as the “Consent”. The field setup should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fcreate-consent-field.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fcreate-consent-field.png" alt="Create a new consent field" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the “Save” button and then repeat this process for the email and phone consents.&lt;/p&gt;

&lt;p&gt;We’ll also need to add a custom field to capture the user’s physical address. We’ll create just one field here. In a production app, you might want to break down the address into a few fields, for example, “Address Line 1”, “Address Line 2”, “City”, etc.&lt;/p&gt;

&lt;p&gt;Click the green “+” button to create a new field. Name the new field “Physical Mail Address”. For the “Field”, select “Custom user data (user.data.*)”. This will present another input box, starting with “user.data”. Type “physicalmailaddress” in this box.&lt;/p&gt;

&lt;p&gt;This adds a custom field to the user’s “data” object. The “data” object can store extra profile information about the user, typically information you’d want to share across all your apps. If the field is app-specific, then you can use the field type “Custom registration data” instead.&lt;/p&gt;

&lt;p&gt;The field setup should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fphysical-mail-field.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fphysical-mail-field.png" alt="Create the physical mail address form field" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click “Save”. We have set up all the custom fields we need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the custom registration form
&lt;/h2&gt;

&lt;p&gt;Now that we have the custom fields, we can create a custom registration form with them. Navigate to Customizations &amp;gt; Forms. Click the green “+” to create a new form.&lt;/p&gt;

&lt;p&gt;Name the form something like “Consents Registration Form”. Make sure “Registration” is selected as the form’s “Type”. Then click the “Add Step” button. A step in a form is like a page of the form. It helps to break up a form into multiple smaller pages so that users are not overwhelmed by a screen full of inputs.&lt;/p&gt;

&lt;p&gt;In the “Step 1” section, click the “Add Field” button. Select “Email” from the “Field” dropdown, and then click “Submit”. Then click “Add Field” again, select “Password” as the “Field”, and click “Submit”. We’ll only capture the user’s email and password in the first step.&lt;/p&gt;

&lt;p&gt;Now click the “Add Step” button again. In this step, we’ll capture the user’s alternative contact information. Click “Add Field”, select “Mobile Phone” as the field, and click “Submit”. Repeat for “Physical Mail Address”.&lt;/p&gt;

&lt;p&gt;Click the “Add Step” button once more for the final registration step. In this step, we’ll capture the user’s consent permission for each marketing channel. Click “Add Field”, select “Physical Mail Marketing Consent”, and then click “Submit”. Repeat this process for the email and phone marketing consents.&lt;/p&gt;

&lt;p&gt;Your final form should look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fregistration-form.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fregistration-form.png" alt="The custom registration form configuration" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click “Save”. The custom registration form is complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding description for the consent inputs.
&lt;/h2&gt;

&lt;p&gt;By default, when adding a consent field type to a form, FusionAuth will render just the &lt;code&gt;id&lt;/code&gt; of the consent on the form. This would not be helpful to users. We can customize the rendered text of the consent fields by adding lines to the “messages” template of the theme, linking a display name to each consent.&lt;/p&gt;

&lt;p&gt;To prepare, navigate to Settings &amp;gt; Consents. For each of the consents you created, record their &lt;code&gt;Id&lt;/code&gt; and &lt;code&gt;Name&lt;/code&gt;. We’ll use this in the theme messages.&lt;/p&gt;

&lt;p&gt;The default theme in FusionAuth cannot be modified, but we can clone the theme and modify our copy.&lt;/p&gt;

&lt;p&gt;Navigate to Customizations &amp;gt; Themes and click the “Duplicate” button next to the default “FusionAuth” theme. Give this new theme a name, perhaps “Consents App Theme”. Click on the “Messages” template and then the “Edit” button next to the default localization. Scroll all the way to the bottom of the template and add a line for each of the consents, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;consents['ef1b3adf-4963-4eee-893a-f16d9d97a95d']=I'd like updates via Snail Mail consents['41bc0627-5df3-466a-bac1-a12925580c7f']=I'd like updates via Email consents['e6a4e555-f037-4e77-92fd-d805bdba7c33']=I'd like updates via Phone (text) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the UUID in each consent with the &lt;code&gt;Id&lt;/code&gt; you recorded earlier. You can make the descriptions whatever you like. Click “Submit”, and then save the theme.&lt;/p&gt;

&lt;p&gt;Now navigate to the application you created earlier. Click “Edit” to open the application editor, and select your new theme from the “Theme” dropdown. Save the application to reflect this change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the profile update form
&lt;/h2&gt;

&lt;p&gt;Once a user has registered for your app, they might want to change their profile information, including the permissions given for each of the consents. FusionAuth provides a default “Account” page out of the box, but we’ll need to create a custom one to account for the consent fields and other custom profile information.&lt;/p&gt;

&lt;p&gt;Navigate to Customizations &amp;gt; Forms and click the green “+” button. Set the “Name” of the form to something like “Consents Self Service”. Select “Self-Service User” from the “Type” dropdown.&lt;/p&gt;

&lt;p&gt;Instead of “Steps” like the registration form, self-service profile forms have “Sections”, which are functionally the same. We’ll mirror the registration layout here.&lt;/p&gt;

&lt;p&gt;Click “Add Section” and add the fields “Email” and “Password” to this section.&lt;/p&gt;

&lt;p&gt;Create another section and add the fields “Physical Mail Address” and “Mobile Phone” to it.&lt;/p&gt;

&lt;p&gt;Now create the last section and add the consent fields “Phone Marketing Consent”, “Email Marketing Consent”, and “Physical Mail Marketing Consent”.&lt;/p&gt;

&lt;p&gt;The form configuration should look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fself-service-form.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fself-service-form.png" alt="The custom self service form configuration" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click “Save” to finish creating the form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linking the new forms to the application
&lt;/h2&gt;

&lt;p&gt;We’ve created the forms but we still need to link them to the application so that they are used in place of the defaults. We also need to enable registrations on the application.&lt;/p&gt;

&lt;p&gt;Navigate to Applications and click on the “Edit” button next to the application created earlier. Click on the “Registration” tab. Under “Self-service registration”, turn on the “Enabled” switch. Select “Advanced” for the “Type”. Select your custom registration form from the “Form” dropdown.&lt;/p&gt;

&lt;p&gt;Under the “Form settings” section, select your custom self-service form from the “User Self-service” dropdown.&lt;/p&gt;

&lt;p&gt;Click “Save” to apply the updates to your application.&lt;/p&gt;

&lt;p&gt;The FusionAuth setup is now complete. We can move on to the Express app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Express
&lt;/h2&gt;

&lt;p&gt;To get started, you should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scaffold a new Express application.&lt;/li&gt;
&lt;li&gt;Install the scaffolded dependencies.&lt;/li&gt;
&lt;li&gt;Install Passport and helper libraries, and the FusionAuth Typescript client.&lt;/li&gt;
&lt;li&gt;Start the server to ensure everything is installed and working.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are the commands to do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx express-generator --view=hbs fusion-consents cd fusion-consents npm install npm install passport passport-oauth2 connect-ensure-login express-session @fusionauth/typescript-client npm start 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all went well, the server should start successfully and you can visit &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fexpress-server.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fexpress-server.png" alt="Express app default home page" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the application
&lt;/h2&gt;

&lt;p&gt;Our application will only have three pages, including the FusionAuth login page.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A home page - a public page showing how many users our app has and inviting users to log in.&lt;/li&gt;
&lt;li&gt;The registrations page (redirected to FusionAuth) with options to set their marketing consents.&lt;/li&gt;
&lt;li&gt;A logged-in private profile page. This will display the user’s profile and consent permissions retrieved from FusionAuth, and allow them to click through to update their profile information, including consent permissions.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Adding and initializing dependencies
&lt;/h2&gt;

&lt;p&gt;To start, we’ll add the references needed for &lt;a href="https://www.passportjs.org" rel="noopener noreferrer"&gt;Passport&lt;/a&gt;, the &lt;a href="https://www.passportjs.org/packages/passport-oauth2/" rel="noopener noreferrer"&gt;passport-oauth2&lt;/a&gt; strategy, and enabling sessions using &lt;a href="https://github.com/expressjs/session" rel="noopener noreferrer"&gt;Express Sessions&lt;/a&gt;. We’ll also add a reference to the &lt;a href="https://www.npmjs.com/package/@fusionauth/typescript-client" rel="noopener noreferrer"&gt;FusionAuth typescript&lt;/a&gt; client.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;app.js&lt;/code&gt; file. Below the line &lt;code&gt;var logger = require('morgan');&lt;/code&gt; add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var passport = require("passport"); var OAuth2Strategy = require("passport-oauth2").Strategy; var passOAuth = require("passport-oauth2"); var { FusionAuthClient } = require("@fusionauth/typescript-client"); var session = require("express-session"); const { ensureLoggedIn } = require('connect-ensure-login'); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can initialize and add Passport and the session handler to the Express pipeline. Add the following just below the &lt;code&gt;app.use(express.static(path.join(__dirname, 'public')));&lt;/code&gt; line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.use(session({ secret: "TOPSECRET" })); app.use(passport.initialize()); app.use(passport.session()); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the &lt;code&gt;TOPSECRET&lt;/code&gt; value with a long, high entropy, random string of your choice. This secret is used to sign the session information in the cookie. Normally, this is kept secret, as anyone who has access to the secret could construct a session cookie that looks legitimate to the server and gives them access to any account on the server. You can also add an environment variable to store this secret, rather than storing it in the code repo.&lt;/p&gt;

&lt;p&gt;We’ll also need to initialize the FusionAuth client with the API key created earlier. This will allow us to retrieve the user profile and consents from FusionAuth after a successful login. Add the following code just below the previous code added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fusionClient = new FusionAuthClient( "&amp;lt;YOUR_FUSION_API_KEY&amp;gt;", "https://&amp;lt;YOUR_FUSIONAUTH_URL&amp;gt;" ); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the parameter &lt;code&gt;&amp;lt;YOUR_FUSIONAUTH_URL&amp;gt;&lt;/code&gt; with the URL your FusionAuth instance is located at (normally &lt;code&gt;http://localhost:9011&lt;/code&gt; for local Docker installs). Replace &lt;code&gt;&amp;lt;YOUR_FUSION_API_KEY&amp;gt;&lt;/code&gt; with the API key created earlier.&lt;/p&gt;

&lt;p&gt;Now we can initialize the Passport strategy. We’ll be connecting to FusionAuth using OAuth2, so we’ll use the passport-oauth2 strategy. Add the following code directly below the code you’ve just added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;passport.use( new OAuth2Strategy( { authorizationURL: "https://&amp;lt;YOUR_FUSIONAUTH_URL&amp;gt;/oauth2/authorize", tokenURL: "https://&amp;lt;YOUR_FUSIONAUTH_URL&amp;gt;/oauth2/token", clientID: "&amp;lt;YOUR_FUSIONAUTH_APP_CLIENTID&amp;gt;", clientSecret: "&amp;lt;YOUR_FUSIONAUTH_APP_CLIENT_SECRET&amp;gt;", callbackURL: "http://localhost:3000/auth/callback", }, function (accessToken, refreshToken, profile, cb) { // Get the user profile from Fusion: fusionClient .retrieveUserUsingJWT(accessToken) .then((clientResponse) =&amp;gt; { return cb(null, clientResponse.response.user); }) .catch((err) =&amp;gt; { console.error(err); }); } ) ); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the parameter &lt;code&gt;&amp;lt;YOUR_FUSIONAUTH_URL&amp;gt;&lt;/code&gt; with the URL your FusionAuth instance is located at. Replace &lt;code&gt;&amp;lt;YOUR_FUSIONAUTH_APP_CLIENTID&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;YOUR_FUSIONAUTH_APP_CLIENT_SECRET&amp;gt;&lt;/code&gt; with the values you saved during the FusionAuth application setup earlier.&lt;/p&gt;

&lt;p&gt;This snippet of code sets up the OAuth parameters for the Passport strategy. The strategy has a callback, which is invoked when a successful authorization and token call has been completed to FusionAuth. The FusionAuth client has a handy method to retrieve a user by the JWT returned from the authorization process. We can use this to get the user and return it to the Passport strategy callback. This user will then also be passed to our session handler to save, and added to the &lt;code&gt;req&lt;/code&gt; parameter in subsequent middleware handlers as &lt;code&gt;req.user&lt;/code&gt;. To enable this, add the following code, below the code you just added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;passport.serializeUser(function (user, done) { process.nextTick(function () { done(null, user); }); }); passport.deserializeUser(function (user, done) { process.nextTick(function () { done(null, user); }); }); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Adding Express routes
&lt;/h1&gt;

&lt;p&gt;We’ve got the basic framework and authorization code set up. Now we can add some routes. We’ll start with the &lt;code&gt;login&lt;/code&gt; route to handle the redirect to FusionAuth.&lt;/p&gt;

&lt;p&gt;Add this code under the &lt;code&gt;app.use("/", indexRouter);&lt;/code&gt; line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.get("/login", passport.authenticate("oauth2")); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we don’t need to add a router or view for the login redirect to FusionAuth to work. Passport will check whether the user needs to be logged in, and if so will send them to FusionAuth for authentication.&lt;/p&gt;

&lt;p&gt;After authentication, FusionAuth will redirect to the callback route we provided in the Passport OAuth setup, as well as in the authorized callback route earlier. We can add this route now. Add the following code under the &lt;code&gt;login&lt;/code&gt; route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.get( "/auth/callback", passport.authenticate("oauth2", { failureRedirect: "/" }), function (req, res) { // Successful authentication, redirect home. res.redirect("/"); } ); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On successful authentication or failure, we’ll redirect to the homepage. Let’s update that now to show the login status and provide a link to the user’s profile page. Open the &lt;code&gt;index.js&lt;/code&gt; file in the &lt;code&gt;routes&lt;/code&gt; folder and update the &lt;code&gt;get&lt;/code&gt; route to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;router.get('/', function(req, res, next) { res.render('index', { title: 'Express', "authenticated": req.isAuthenticated() }); }); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passport adds a function &lt;code&gt;isAuthenticated()&lt;/code&gt; to the &lt;code&gt;req&lt;/code&gt; object. Querying this function tells us whether the user is logged in. We add this to the keys and values passed to the index view, so that we can show a different message based on the user’s authentication status.&lt;/p&gt;

&lt;p&gt;Now open the &lt;code&gt;index.hbs&lt;/code&gt; file in the &lt;code&gt;views&lt;/code&gt; folder, and update the code to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1&amp;gt;{{title}}&amp;lt;/h1&amp;gt; &amp;lt;p&amp;gt;Welcome to {{title}}&amp;lt;/p&amp;gt; {{#if authenticated}} &amp;lt;p&amp;gt;You are logged in!&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt; You can now visit your &amp;lt;a href="/users/me"&amp;gt;profile page&amp;lt;/a&amp;gt; &amp;lt;/p&amp;gt; {{else}} &amp;lt;p&amp;gt; &amp;lt;a href="/login"&amp;gt;Login Here&amp;lt;/a&amp;gt; &amp;lt;/p&amp;gt; {{/if}} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will notify the user if they are logged in or not, and point them to the relevant action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a members-only area
&lt;/h2&gt;

&lt;p&gt;Now that we have the basic login and authentication mechanics set up, we can add a restricted route that is only available to users that are logged in. This route will show the user their profile, and a link to update their information.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;users.js&lt;/code&gt; file in the &lt;code&gt;routes&lt;/code&gt; folder, replace the code with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var express = require("express"); var router = express.Router(); var { FusionAuthClient } = require("@fusionauth/typescript-client"); const fusionClient = new FusionAuthClient( "&amp;lt;YOUR_FUSION_API_KEY&amp;gt;", "https://&amp;lt;YOUR_FUSIONAUTH_URL&amp;gt;" ); /* GET users listing. */ router.get("/me", async function (req, res, next) { const user = req.user; // Get the user's consent info: let userConsents = {}; try { const response = await fusionClient.retrieveUserConsents(user.id); userConsents = response.response.userConsents; } catch (err) { console.log(err); } res.render("me", { profile: JSON.stringify(user, null, "\t"), consents: JSON.stringify(userConsents, null, "\t"), }); }); module.exports = router; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the parameter &lt;code&gt;&amp;lt;YOUR_FUSIONAUTH_URL&amp;gt;&lt;/code&gt; with the URL your FusionAuth instance is located at (normally &lt;code&gt;http://localhost:9011&lt;/code&gt; for local Docker installs). Replace &lt;code&gt;&amp;lt;YOUR_FUSION_API_KEY&amp;gt;&lt;/code&gt; with the API key created earlier.&lt;/p&gt;

&lt;p&gt;This code sets up a FusionAuth client link so that we can read the user’s consent information from &lt;a href="https://fusionauth.io/docs/v1/tech/apis/consents#retrieve-a-user-consent" rel="noopener noreferrer"&gt;the API&lt;/a&gt;. It also creates a &lt;code&gt;/users/me&lt;/code&gt; route, which is used to retrieve the user’s profile information. In the route, we grab the &lt;code&gt;user&lt;/code&gt; object from the &lt;code&gt;req&lt;/code&gt; parameter. Recall this was added by Passport earlier in the setup. Then we make a call to the FusionAuth API to retrieve the user’s consent information, passing in the user’s &lt;code&gt;id&lt;/code&gt; as the parameter. We stringify the user object and consents and send them to the &lt;code&gt;me&lt;/code&gt; handlebars template to render. In a production app, you’d want to display this a bit more nicely and maybe search for the specific fields and consents you want to display. You’d access a user’s consents exactly the same way, through the API, when determining what kind of marketing channel they’d prefer. This would typically be called in a background worker process or serverless function.&lt;/p&gt;

&lt;p&gt;We need to create the handlebars template for this route. Create a new file in the “views” folder, called &lt;code&gt;me.hbs&lt;/code&gt;. Add the following code to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1&amp;gt;{{title}}&amp;lt;/h1&amp;gt; &amp;lt;p&amp;gt;Welcome to {{title}}&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt; You can update your &amp;lt;a target="_blank" href="&amp;lt;YOUR_FUSIONAUTH_ACCOUNT_LINK&amp;gt;" &amp;gt;profile here&amp;lt;/a&amp;gt; &amp;lt;/p&amp;gt; &amp;lt;h2&amp;gt;Profile Info&amp;lt;/h2&amp;gt; &amp;lt;p&amp;gt; {{profile}} &amp;lt;/p&amp;gt; &amp;lt;h2&amp;gt;Consent Permissions&amp;lt;/h2&amp;gt; &amp;lt;p&amp;gt; {{consents}} &amp;lt;/p&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a template that has placeholders for the dump of the raw user information and the user consents.&lt;/p&gt;

&lt;p&gt;There is also a link for the user to update their information on FusionAuth, using the custom self-service form created earlier. To update the value for &lt;code&gt;&amp;lt;YOUR_FUSIONAUTH_ACCOUNT_LINK&amp;gt;&lt;/code&gt;, navigate to Applications in FusionAuth. Click the “View” button next to your application, and scroll down to the “Account URL” value. Copy the URL and replace &lt;code&gt;&amp;lt;YOUR_FUSIONAUTH_ACCOUNT_LINK&amp;gt;&lt;/code&gt; in the code above with it.&lt;/p&gt;

&lt;p&gt;Now we need to secure the route to this profile page for users that are authenticated. To help with that, we’ll use the &lt;a href="https://github.com/jaredhanson/connect-ensure-login" rel="noopener noreferrer"&gt;&lt;code&gt;connect-ensure-login&lt;/code&gt;&lt;/a&gt; middleware we installed earlier. Update the &lt;code&gt;users&lt;/code&gt; route in the &lt;code&gt;app.js&lt;/code&gt; file from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.use('/users', usersRouter); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.use('/users', ensureLoggedIn('/login'), usersRouter); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ensureLoggedIn&lt;/code&gt; middleware checks if the user is authenticated before proceeding to the router (or following middleware). It redirects to the &lt;code&gt;login&lt;/code&gt; page if the user is not logged in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;We are done with the coding. Type &lt;code&gt;npm start&lt;/code&gt; at the console to start up the server. Then navigate to &lt;code&gt;localhost:3000&lt;/code&gt;, preferably in a private tab. This ensures that your main admin login to FusionAuth is not a confounding factor while logging in.&lt;/p&gt;

&lt;p&gt;You should see the main page looking something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fnot-logged-in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fnot-logged-in.png" alt="The main page when logged out" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on “Login Here” should redirect you to your FusionAuth installation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Flogin-page.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Flogin-page.png" alt="The FusionAuth login page" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking the “Create an account” link should render the custom registration form configured earlier. Notice that it has three steps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fregistration-steps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fregistration-steps.png" alt="The custom registration page, with multiple steps" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter all the information and click “Register” at the end of the steps. You should be redirected to your Express app, with a new message on the home page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Flogged-in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Flogged-in.png" alt="The root page message for logged in users" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on the “profile page” link should take you to &lt;code&gt;users/me&lt;/code&gt;, showing two JSON objects representing your profile on FusionAuth, along with the raw data from the consents API. Notice in each consent that there is a property &lt;code&gt;status&lt;/code&gt;. This will be either &lt;code&gt;active&lt;/code&gt; or &lt;code&gt;revoked&lt;/code&gt;. You can use these values when checking to send information to the user through each channel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fusers-me.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ffusionauth.io%2Fassets%2Fimg%2Fblogs%2Fconsents-app%2Fusers-me.png" alt="The users/me page showing the user's FusionAuth profile" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking the “profile page” link will redirect to FusionAuth, where the user can view and update their information and consent permissions via the self-service form created earlier. Once navigated to the FusionAuth-hosted profile page, clicking on the “Edit” pencil icon button in the top right will allow the user to update their profile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to next with Express and FusionAuth?
&lt;/h2&gt;

&lt;p&gt;That’s the basics of our Express and FusionAuth app. The app has a fully featured authentication system, along with user consents, without the hassle and possible risks of implementing all of that code ourselves. The complete code is hosted on GitHub &lt;a href="https://github.com/fusionauth/fusionauth-example-express-consents" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a production environment, you would need to do a bit more work to lock down FusionAuth. In our example, we used the default password provided with Docker for our database, left debug mode on, and ran FusionAuth locally, co-hosted with our Express application. For a safer setup, you would run FusionAuth on its own infrastructure, physically separate from the Express app, and take more care around production configuration and deployment.&lt;/p&gt;

&lt;p&gt;You would also need to add more interesting features to this app for it to be useful. But being able to take care of the authentication, consents, and general security with just a small amount of configuration code leaves a lot more time for your application’s more useful and critical features.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>express</category>
      <category>node</category>
      <category>permissions</category>
    </item>
  </channel>
</rss>
