<?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>DEV Community: MonoCloud Admin</title>
    <description>The latest articles on DEV Community by MonoCloud Admin (@monocloud_admin).</description>
    <link>https://dev.to/monocloud_admin</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%2F3830042%2F5158fbd7-8dcb-4833-afd4-e8628d3d3162.png</url>
      <title>DEV Community: MonoCloud Admin</title>
      <link>https://dev.to/monocloud_admin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/monocloud_admin"/>
    <language>en</language>
    <item>
      <title>Protecting Node.js APIs: Audiences, Scopes, and Bearer Tokens</title>
      <dc:creator>MonoCloud Admin</dc:creator>
      <pubDate>Mon, 27 Apr 2026 12:41:12 +0000</pubDate>
      <link>https://dev.to/monocloud_admin/protecting-nodejs-apis-audiences-scopes-and-bearer-tokens-1dg3</link>
      <guid>https://dev.to/monocloud_admin/protecting-nodejs-apis-audiences-scopes-and-bearer-tokens-1dg3</guid>
      <description>&lt;p&gt;Most APIs leak in the same handful of ways: missing audience checks, scope rules scattered across handlers, JWT validation that drifts out of date, tokens issued for one service quietly accepted by another. Each one is small on its own, and each one ends up in a postmortem.&lt;/p&gt;

&lt;p&gt;This post walks through the patterns that prevent those failures — token validation done right, per-API audience isolation, route-level scope enforcement, and server-side token forwarding from a front end. The code samples come from &lt;a href="https://github.com/monocloud/monoshop-api-demo" rel="noopener noreferrer"&gt;MonoShop&lt;/a&gt;, a small reference repo we put together to make the patterns concrete, but the ideas apply to any Node.js API sitting behind an OAuth/OIDC provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat Each API as Its Own Resource
&lt;/h2&gt;

&lt;p&gt;The first design decision in API authorization is granularity: what is a token actually issued &lt;em&gt;for&lt;/em&gt;? The right answer is almost always &lt;em&gt;each API&lt;/em&gt;, not "the platform."&lt;/p&gt;

&lt;p&gt;If your billing API and your admin API both accept "any signed token from our IDP," they share a trust boundary. Compromise one and you've compromised both. The fix is to register each API as a separate protected resource with its own audience identifier — typically a URL string — and reject anything that doesn't match.&lt;/p&gt;

&lt;p&gt;The MonoShop repo demonstrates this with two services:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Audience&lt;/th&gt;
&lt;th&gt;Required Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Product API&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://api.monoshop.com/products&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;read:products&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invoice API&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://api.monoshop.com/invoices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;read:invoices&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A token minted for one will not validate against the other, even though both trust the same identity provider. The audience claim is just a string, but it's the line between "user signed in" and "user can hit this specific service." Treat it like a service boundary, because it is one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token Validation, Done Right
&lt;/h2&gt;

&lt;p&gt;Validating a JWT properly is more than calling &lt;code&gt;jwt.verify&lt;/code&gt;. You need the right signing keys (fetched from the provider's JWKS endpoint), a strategy for key rotation, issuer verification, audience verification, expiry and clock-skew handling, and scope checks tailored to each route. Get any of it wrong and you've either broken legitimate users or, worse, accepted tokens you shouldn't.&lt;/p&gt;

&lt;p&gt;This is the layer where silent bugs live. Most identity providers ship middleware that handles the pipeline end-to-end, and there are certified OAuth libraries that do the same — reaching for one of them is the simpler and safer default. Here's what that looks like — the entire Product API in MonoShop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;protectApi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@monocloud/backend-node/express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;protect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;protectApi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;protect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read:products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4001&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The middleware fetches the JWKS, verifies the signature, checks the issuer, validates expiry, enforces the configured audience, and confirms the token carries the scopes the endpoint requires.&lt;/p&gt;

&lt;p&gt;If you're using opaque tokens instead of JWTs, it introspects the token against the provider's introspection endpoint and runs the same checks against the response, including confirming the token hasn't been revoked. Your handler runs only when all of that passes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audience Isolation Should Be Configuration
&lt;/h2&gt;

&lt;p&gt;Once each API is its own resource, enforcing the boundary should be a config value, not application code. A typical setup is one environment variable read by the middleware at startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MONOCLOUD_BACKEND_AUDIENCE=http://api.monoshop.com/products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The check happens before any of your code runs, which means you can't accidentally forget it on a new route. Same user, same issuer, same signing key — wrong audience, no access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scopes Belong Next to Routes
&lt;/h2&gt;

&lt;p&gt;Authentication tells you &lt;em&gt;who&lt;/em&gt; the caller is. Scopes tell you &lt;em&gt;what they're allowed to do&lt;/em&gt;. The cleanest place to express that is right next to the route the rule applies to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="nf"&gt;protect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read:products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;  &lt;span class="nx"&gt;listProducts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;protect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;write:products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="nx"&gt;createProduct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A token without &lt;code&gt;read:products&lt;/code&gt; gets a &lt;code&gt;403&lt;/code&gt; before &lt;code&gt;listProducts&lt;/code&gt; is invoked. There's no &lt;code&gt;if (user.can(...))&lt;/code&gt; check inside the handler, no role table to keep in sync — the authorization rule is a property of the route, not a runtime branch.&lt;/p&gt;

&lt;p&gt;The general principle: keep authorization declarative and visible at the boundary. If you can't read your route table and tell who's allowed to call what, you've buried the rules somewhere they'll fall out of sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Tokens Server-Side
&lt;/h2&gt;

&lt;p&gt;Bearer tokens are credentials. Anywhere you store them on the client - local storage, session storage, accessible cookies — you've extended the attack surface to include XSS anywhere on your front end, plus every third-party script you've ever loaded.&lt;/p&gt;

&lt;p&gt;The safer pattern, especially with frameworks that have a server runtime, is to keep tokens in a server-side session and only forward them to APIs from server code. The Next.js front end in MonoShop uses a Server Action to do exactly that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getTokens&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@monocloud/auth-nextjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getTokens&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_PRODUCT_API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/products`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser never sees the access token. It calls the Server Action; the server retrieves the token from the session, attaches it to the upstream request, and returns the result. The same shape works whether you have one API or ten — the front end requests tokens for the resources it needs at sign-in time, and the provider mints one access token per audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling Out Without New Infrastructure
&lt;/h2&gt;

&lt;p&gt;The pattern above scales naturally. Each new service is another protected resource with its own audience and scopes. Every API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trusts the same identity provider&lt;/li&gt;
&lt;li&gt;Validates its own audience&lt;/li&gt;
&lt;li&gt;Enforces its own scopes&lt;/li&gt;
&lt;li&gt;Has no awareness of the others&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no shared session store to coordinate, no internal token-exchange dance, no "auth service" in your dependency graph that everyone has to keep alive. Each API is independently protected by stateless token validation. MonoShop ships with two services to show the multi-API shape, but the third would be a copy-paste pointed at a new audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying It Locally
&lt;/h2&gt;

&lt;p&gt;If you want to see the patterns in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/monocloud/monoshop-api-demo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;monoshop-api-demo
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy each &lt;code&gt;.env.example&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt; and fill in credentials from your identity provider (the repo uses &lt;a href="https://www.monocloud.com" rel="noopener noreferrer"&gt;MonoCloud&lt;/a&gt; — sign up is free if you want to follow along), then:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Open &lt;em&gt;&lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/em&gt;, sign in, and the dashboard will call both protected APIs and render their data. To confirm the protection actually works, hit the APIs directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:4001/products
&lt;span class="c"&gt;# 401 Unauthorized&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with a token whose audience or scope doesn't match — same result. The middleware does its job before your handler ever sees the request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;The patterns worth carrying away, regardless of which provider you pick:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treat each API as a separate resource with its own audience. Don't share tokens across services.&lt;/li&gt;
&lt;li&gt;Enforce scopes at the route, not in the handler. Your authorization rules should be readable in the route table.&lt;/li&gt;
&lt;li&gt;Keep tokens server-side. If you have a server runtime, forward them from there rather than handing them to the browser.&lt;/li&gt;
&lt;li&gt;Don't roll your own JWT validation in production unless you're prepared to maintain it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ship Auth, Don't Build It
&lt;/h2&gt;

&lt;p&gt;API authorization isn't usually where teams want to spend roadmap time. The patterns above are well-trodden, and the implementation is mostly maintenance work that doesn't differentiate your product. Pick a provider, lean on a vetted library, and let the boring parts stay boring.&lt;/p&gt;

&lt;p&gt;Want to dig deeper? Start with the &lt;strong&gt;&lt;a href="https://www.monocloud.com/docs" rel="noopener noreferrer"&gt;MonoCloud Docs&lt;/a&gt;&lt;/strong&gt;, explore the &lt;strong&gt;&lt;a href="https://www.monocloud.com/docs/sdks/express-backend/index" rel="noopener noreferrer"&gt;Express Backend SDK&lt;/a&gt;&lt;/strong&gt;, or clone &lt;strong&gt;&lt;a href="https://github.com/monocloud/monoshop-api-demo" rel="noopener noreferrer"&gt;MonoShop&lt;/a&gt;&lt;/strong&gt; and start hacking.&lt;/p&gt;

</description>
      <category>node</category>
      <category>api</category>
      <category>oauth</category>
      <category>security</category>
    </item>
    <item>
      <title>OAuth for SaaS: What Every Developer and Technical Leader Needs to Know</title>
      <dc:creator>MonoCloud Admin</dc:creator>
      <pubDate>Mon, 23 Mar 2026 20:51:36 +0000</pubDate>
      <link>https://dev.to/monocloud/oauth-for-saas-what-every-developer-and-technical-leader-needs-to-know-22o</link>
      <guid>https://dev.to/monocloud/oauth-for-saas-what-every-developer-and-technical-leader-needs-to-know-22o</guid>
      <description>&lt;p&gt;If you're building a SaaS product today, OAuth isn't optional — it's foundational. It's the protocol behind "Sign in with Google," third-party integrations, API access control, and the secure service-to-service communication that holds modern platforms together.&lt;/p&gt;

&lt;p&gt;Yet OAuth remains widely misunderstood. Teams confuse it with authentication, misuse its flows, or bolt it on as an afterthought, only to face security gaps and painful refactors later.&lt;/p&gt;

&lt;p&gt;This guide cuts through the confusion. It explains what OAuth actually does, why SaaS platforms need it, which flows matter for which use cases, and the mistakes that trip up even experienced teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SaaS Applications Can't Avoid OAuth
&lt;/h2&gt;

&lt;p&gt;A modern SaaS platform is rarely a single application. It's typically a constellation of components — a web dashboard, a mobile app, public APIs, third-party integrations, and a cluster of internal microservices — all needing access to shared resources like user data, billing records, and analytics.&lt;/p&gt;

&lt;p&gt;The challenge: how do you let all of these components access the right data, for the right users, without passing around passwords or hardcoding secrets?&lt;/p&gt;

&lt;p&gt;OAuth solves this by &lt;strong&gt;issuing tokens that represent delegated permissions&lt;/strong&gt;. Instead of sharing credentials, an application receives a short-lived token that grants specific, limited access. The user stays in control. The credentials stay safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Roles in Every OAuth Flow
&lt;/h2&gt;

&lt;p&gt;OAuth defines four participants. Understanding who does what is the first step to implementing it correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Owner&lt;/strong&gt; — the user who owns the data. In a SaaS context, this is your customer granting permission for an app to access their account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client&lt;/strong&gt; — the application requesting access. This could be your web frontend, a mobile app, a CLI tool, or a third-party integration built on your API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authorization Server&lt;/strong&gt; — the system that authenticates users, enforces permissions, and issues tokens. This is the engine of the whole flow. It handles consent screens, validates credentials, and decides what access to grant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Server&lt;/strong&gt; — the API that holds protected data. It receives requests, validates the attached token's signature, expiration, audience, and scopes, and then either allows or rejects the request.&lt;/p&gt;

&lt;p&gt;In practice, many SaaS teams run the authorization server as a managed service and distribute resource servers across their API landscape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access Tokens: The Core Mechanism
&lt;/h2&gt;

&lt;p&gt;At the heart of OAuth is a simple idea: instead of giving an application a user's password, you give it a &lt;strong&gt;token&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That token represents a specific set of permissions, is scoped to a specific API, and expires after a defined period. The API receiving the token validates it and grants access accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach decouples authentication from authorization. The API doesn't need to know how the user signed in — it only needs to know that the token is valid and carries the right permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scopes: Fine-Grained Permission Control
&lt;/h2&gt;

&lt;p&gt;Scopes define what a client application is allowed to do with a token.&lt;/p&gt;

&lt;p&gt;A well-designed SaaS API uses granular scopes rather than broad, all-or-nothing permissions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;What It Allows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;read:products&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View the product catalog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;write:products&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create or update products&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;read:invoices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View invoice data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When a token arrives at your API, the resource server checks whether its scopes match the operation being requested. A token with &lt;code&gt;read:products&lt;/code&gt; can't update anything — the request gets rejected.&lt;/p&gt;

&lt;p&gt;This model lets SaaS platforms implement nuanced permission systems across multiple APIs without reinventing access control for each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  OAuth vs. API Keys vs. Session Cookies
&lt;/h2&gt;

&lt;p&gt;OAuth isn't the only way to secure access. Here's how it compares to the other common approaches:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9189x6ljo3yolnoo8178.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9189x6ljo3yolnoo8178.png" alt="OAuth Tokens vs API Keys vs Session Cookies" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API keys&lt;/strong&gt; are simple and useful for basic integrations, but they represent an application rather than a user, and offer no built-in permission model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session cookies&lt;/strong&gt; work well for traditional web apps, but they're tied to browser sessions and don't translate to API-first architectures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OAuth tokens&lt;/strong&gt; are purpose-built for the kind of distributed, multi-client, API-driven systems that SaaS platforms actually are.&lt;/p&gt;

&lt;h2&gt;
  
  
  OAuth vs. OpenID Connect: Authorization vs. Authentication
&lt;/h2&gt;

&lt;p&gt;One of the most common points of confusion: &lt;strong&gt;OAuth is not an authentication protocol&lt;/strong&gt;. It's an authorization framework.&lt;/p&gt;

&lt;p&gt;OAuth answers the question: &lt;em&gt;"What is this application allowed to do?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Authentication — proving &lt;em&gt;who the user is&lt;/em&gt; — is handled by &lt;strong&gt;OpenID Connect (OIDC)&lt;/strong&gt;, which is a layer built on top of OAuth.&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;OAuth&lt;/th&gt;
&lt;th&gt;OIDC&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Token&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Access Token&lt;/td&gt;
&lt;td&gt;ID Token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Answers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"What can this app do?"&lt;/td&gt;
&lt;td&gt;"Who is this user?"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most SaaS platforms need both. OIDC handles user login and identity. OAuth handles API access and delegated permissions. Together, they form the standard identity and authorization stack for modern applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right OAuth Flow
&lt;/h2&gt;

&lt;p&gt;Different client types require different OAuth flows. The choice depends on where the client runs, whether it can securely store secrets, and whether a user is involved.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiezxmvntj4jgd4zelzs1.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiezxmvntj4jgd4zelzs1.png" alt="Which OAuth Flow Should You Use?" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Authorization Code Flow
&lt;/h3&gt;

&lt;p&gt;The gold standard for web applications. The client exchanges a short-lived authorization code for tokens on the backend, keeping tokens out of the browser entirely. Ideal for SaaS dashboards and server-rendered applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authorization Code + PKCE
&lt;/h3&gt;

&lt;p&gt;An extension of the authorization code flow designed for clients that can't keep secrets — like single-page apps and mobile apps. PKCE (Proof Key for Code Exchange) adds a cryptographic challenge that binds the authorization request to the token request, preventing interception attacks. This is now the recommended default for any public client.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fic7mqublav41hcxxen5h.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fic7mqublav41hcxxen5h.png" alt="Authorization Code + PKCE Flow" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Client Credentials
&lt;/h3&gt;

&lt;p&gt;Used when no user is involved. The application authenticates with its own credentials and receives a token representing itself. This is the standard flow for service-to-service communication, background jobs, and internal platform APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Device Authorization Flow
&lt;/h3&gt;

&lt;p&gt;Designed for devices with limited input capabilities — smart TVs, IoT devices, CLI tools. The user completes authentication on a separate device by visiting a URL and entering a code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refresh Tokens
&lt;/h3&gt;

&lt;p&gt;Not a standalone flow, but a critical companion to the others. Refresh tokens let applications obtain new access tokens without forcing the user to sign in again, enabling long-lived sessions while keeping access tokens short-lived.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five Mistakes That Trip Up SaaS Teams
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Building it all from scratch
&lt;/h3&gt;

&lt;p&gt;Implementing an authorization server, token lifecycle management, scope enforcement, and OIDC compliance is a significant engineering investment — and a risky one if security isn't your team's core expertise. Managed identity platforms exist precisely to solve this problem reliably.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Overly broad scopes
&lt;/h3&gt;

&lt;p&gt;A scope called &lt;code&gt;admin&lt;/code&gt; that grants access to everything defeats the purpose of scoped authorization. Use granular scopes like &lt;code&gt;read:products&lt;/code&gt; and &lt;code&gt;write:orders&lt;/code&gt;. If a token is compromised, the blast radius stays contained.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Long-lived access tokens
&lt;/h3&gt;

&lt;p&gt;Access tokens should expire in minutes, not hours or days. A typical lifetime is &lt;strong&gt;5 to 60 minutes&lt;/strong&gt;. Refresh tokens handle renewal. Short-lived tokens limit the damage window if one is leaked.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Missing audience restrictions
&lt;/h3&gt;

&lt;p&gt;Tokens should specify which API they're intended for. Without audience validation, a token issued for your billing API could be replayed against your admin API. Always set and validate the &lt;code&gt;aud&lt;/code&gt; claim.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Treating login as authorization
&lt;/h3&gt;

&lt;p&gt;A successful user login doesn't mean every API should accept every request. APIs should still validate audience, scopes, and token type independently. Authentication tells you who someone is; authorization tells you what they're allowed to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Go From Here
&lt;/h2&gt;

&lt;p&gt;OAuth has become the standard authorization model for modern SaaS platforms. It gives teams a consistent, secure way to protect APIs, enable integrations, and manage access across users, applications, and services.&lt;/p&gt;

&lt;p&gt;Getting it right means choosing the correct flows for your client types, designing granular scopes, keeping tokens short-lived, and validating them properly at every API boundary.&lt;/p&gt;

&lt;p&gt;If you'd rather focus on building your product than managing OAuth infrastructure, &lt;a href="https://www.monocloud.com?utm_source=devto&amp;amp;utm_medium=oauth_for_saas" rel="noopener noreferrer"&gt;MonoCloud&lt;/a&gt; provides a complete authentication and authorization platform built for SaaS — handling OAuth, OIDC, user management, and access control so your team doesn't have to build and maintain it from scratch.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>identity</category>
      <category>sass</category>
      <category>oauth</category>
    </item>
    <item>
      <title>Add Authentication to Next.js With One Line of Code</title>
      <dc:creator>MonoCloud Admin</dc:creator>
      <pubDate>Tue, 17 Mar 2026 20:53:56 +0000</pubDate>
      <link>https://dev.to/monocloud/add-authentication-to-nextjs-with-one-line-of-code-51ed</link>
      <guid>https://dev.to/monocloud/add-authentication-to-nextjs-with-one-line-of-code-51ed</guid>
      <description>&lt;p&gt;&lt;code&gt;export default authMiddleware()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That’s it. One line in your middleware file and your Next.js app has authentication — sign-up, sign-in, sign-out, session management, and protected routes. No wrestling with JWTs, no hand-rolling session logic, no third-party cookie headaches. Just drop it in and move on.&lt;/p&gt;

&lt;p&gt;We built &lt;a href="https://github.com/monocloud/monogrub-nextjs-demo" rel="noopener noreferrer"&gt;MonoGrub&lt;/a&gt;, a sample food ordering app, to show what this looks like in a real project. It’s open source, runs locally in five minutes, and covers the auth patterns you’ll actually need in production. Let’s walk through what it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Line, Full Auth
&lt;/h2&gt;

&lt;p&gt;When you call &lt;code&gt;authMiddleware()&lt;/code&gt;, &lt;a href="https://www.monocloud.com" rel="noopener noreferrer"&gt;MonoCloud&lt;/a&gt; handles the entire OpenID Connect flow behind the scenes — redirects, token exchange, cookie-based sessions, silent refresh. Your users get a polished sign-up and sign-in experience, and you don't write a single line of auth plumbing.&lt;/p&gt;

&lt;p&gt;But the real power shows up when you need more control. Want to lock down your admin section to a specific group of users? Pass a config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;authMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;protectedRoutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s role-based access control, declared at the middleware layer, with zero custom logic in your page components. Add more routes, more groups, more rules — it’s all configuration, not code. By default, unauthorized users get a 403. Want something friendlier? Add an &lt;code&gt;onGroupAccessDenied&lt;/code&gt; handler to redirect them wherever you like — MonoGrub sends them to a custom access-denied page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protecting Routes, APIs and UI
&lt;/h2&gt;

&lt;p&gt;Authentication tells you &lt;em&gt;who&lt;/em&gt; someone is. But most apps need to go further — you need to control &lt;em&gt;what they can do&lt;/em&gt;. MonoGrub demonstrates this across every layer of a Next.js app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server Actions&lt;/strong&gt; — Every sensitive operation in MonoGrub starts with a call to &lt;code&gt;protect()&lt;/code&gt;. This validates the user's session server-side before any business logic runs. If the session is expired or invalid, the user is redirected to the sign-in page. No ambiguity, no race conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Routes&lt;/strong&gt; — MonoGrub’s API endpoints use a &lt;code&gt;protectApi()&lt;/code&gt; helper that gates access by auth status or group membership. The user's identity comes from the server-side session — not from anything the client submits — which eliminates an entire class of spoofing vulnerabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pages&lt;/strong&gt; — For pages that require authentication, MonoGrub wraps them with &lt;code&gt;protectPage()&lt;/code&gt;. If an unauthenticated user tries to access a protected page, they're automatically redirected to sign in — no manual checks or redirect logic needed in your component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI Components&lt;/strong&gt; — Not everything needs a full page redirect. MonoGrub uses the client-side &lt;code&gt;&amp;lt;Protected&amp;gt;&lt;/code&gt; component to conditionally render UI based on auth status. For example, the "Place Order" button is wrapped in &lt;code&gt;&amp;lt;Protected&amp;gt;&lt;/code&gt; with a fallback that prompts the user to sign in. Authenticated users see the button; everyone else sees a sign-in prompt — no &lt;code&gt;useEffect&lt;/code&gt;, no loading flicker, no auth state wiring.&lt;/p&gt;

&lt;p&gt;The pattern is consistent: declare your access rules up front, and let MonoCloud enforce them. Your application code stays focused on what it’s actually supposed to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Profile Management, Without a User Table
&lt;/h2&gt;

&lt;p&gt;Once your users are signed in, they’ll want to manage their accounts. MonoGrub handles profile updates — name changes, password resets — entirely through &lt;a href="https://github.com/monocloud/management-js" rel="noopener noreferrer"&gt;MonoCloud’s Management SDK&lt;/a&gt;. There’s no custom user table to maintain, no password hashing to get right, no “forgot password” flow to build from scratch.&lt;/p&gt;

&lt;p&gt;Updating a user’s name is a single SDK call. Changing a password is another. After any update, we refresh the session so the UI stays in sync.&lt;/p&gt;

&lt;p&gt;This is the kind of feature that’s easy to underestimate. It’s not glamorous, but building it from scratch is months of work you’ll never get back. With MonoCloud, it’s already done.&lt;/p&gt;

&lt;h2&gt;
  
  
  What MonoGrub Covers
&lt;/h2&gt;

&lt;p&gt;To recap, here’s what you’ll find in the sample app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sign-up, sign-in, sign-out&lt;/strong&gt; powered by MonoCloud’s OIDC flow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route protection&lt;/strong&gt; via middleware — both auth-required and role-required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin dashboard&lt;/strong&gt; gated by group membership&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profile management&lt;/strong&gt; using the Management SDK (name updates, password changes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protected API routes&lt;/strong&gt; with server-side identity verification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role-based UI&lt;/strong&gt; — the navbar adapts based on the user’s group membership&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get It Running
&lt;/h2&gt;

&lt;p&gt;Clone the repo, create a free &lt;a href="https://dashboard.monocloud.com/api/auth/signin?prompt=create" rel="noopener noreferrer"&gt;MonoCloud account&lt;/a&gt;, add your credentials, and start the dev server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/monocloud/monogrub-nextjs-demo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;monogrub-nextjs-demo
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;.env.local&lt;/code&gt; file in the project root with your MonoCloud credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MONOCLOUD_AUTH_TENANT_DOMAIN=&amp;lt;your-tenant-domain&amp;gt;
MONOCLOUD_AUTH_CLIENT_ID=&amp;lt;your-client-id&amp;gt;
MONOCLOUD_AUTH_CLIENT_SECRET=&amp;lt;your-client-secret&amp;gt;
MONOCLOUD_AUTH_SCOPES=openid profile email groups
MONOCLOUD_AUTH_APP_URL=http://localhost:3000
MONOCLOUD_AUTH_COOKIE_SECRET=&amp;lt;your-cookie-secret&amp;gt;
MONOCLOUD_AUTH_REFETCH_USER_INFO=true
MONOCLOUD_AUTH_REFETCH_STRICT_PROFILE_SYNC=true
MONOCLOUD_API_KEY=&amp;lt;your-monocloud-api-key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then fire it up:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:3000&lt;/code&gt;, and you’ve got a fully authenticated app running locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ship Auth, Don’t Build It
&lt;/h2&gt;

&lt;p&gt;Every hour you spend building authentication is an hour you’re not spending on your product. MonoCloud gives you production-grade auth with minimal integration effort — start with one line of middleware, and scale up to protected routes, RBAC, and user management as you need them.&lt;/p&gt;

&lt;p&gt;Explore the source, adapt the patterns, and ship.&lt;/p&gt;

&lt;p&gt;Want to dig deeper? Start with the &lt;a href="https://www.monocloud.com/docs" rel="noopener noreferrer"&gt;MonoCloud Docs&lt;/a&gt;, explore the &lt;a href="https://github.com/monocloud/management-js" rel="noopener noreferrer"&gt;Management SDK&lt;/a&gt;, or clone &lt;a href="https://github.com/monocloud/monogrub-nextjs-demo" rel="noopener noreferrer"&gt;MonoGrub&lt;/a&gt; and start hacking.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>oauth</category>
      <category>cybersecurity</category>
      <category>node</category>
    </item>
  </channel>
</rss>
