<?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</title>
    <description>The latest articles on DEV Community by MonoCloud (monocloud).</description>
    <link>https://dev.to/monocloud</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.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F12731%2Fe1224f2a-0427-4f4e-94aa-af659bd2e5da.png</url>
      <title>DEV Community: MonoCloud</title>
      <link>https://dev.to/monocloud</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/monocloud"/>
    <language>en</language>
    <item>
      <title>Next.js Authentication: The Complete 2026 Guide</title>
      <dc:creator>Shivangi Tripathi</dc:creator>
      <pubDate>Fri, 12 Jun 2026 12:03:18 +0000</pubDate>
      <link>https://dev.to/monocloud/nextjs-authentication-the-complete-2026-guide-5bok</link>
      <guid>https://dev.to/monocloud/nextjs-authentication-the-complete-2026-guide-5bok</guid>
      <description>&lt;p&gt;Most Next.js authentication tutorials teach you how to build the plumbing. This one skips that. You don't need to write session logic, token exchange, or JWT verification yourself — and you definitely shouldn't. You need to know where auth lives in a Next.js app, how the App Router changes things, and how to ship it fast without painting yourself into a corner.&lt;/p&gt;

&lt;p&gt;A production Next.js app needs session management that survives page refreshes, protected routes that redirect unauthenticated users without flicker, API endpoints that reject requests without a valid token, Server Actions that verify identity before running any business logic, and a sign-in/sign-out flow that handles the OAuth/OIDC roundtrip.&lt;/p&gt;

&lt;p&gt;Let's go through each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Auth Lives in Next.js App Router
&lt;/h2&gt;

&lt;p&gt;Next.js 13+ separates your code into two runtimes: server and client. Auth decisions should happen on the server. If you're checking auth state in a client component to decide what to render, you're doing it in the wrong place — there will be a flash of unauthenticated content before the check resolves.&lt;/p&gt;

&lt;p&gt;The right model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Middleware / proxy:&lt;/strong&gt; handles route protection at the edge — before the page renders&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server Components:&lt;/strong&gt; access session data directly from the request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Components:&lt;/strong&gt; receive auth state as props from the server, or via a lightweight hook&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because it determines where tokens are stored and how they're validated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Install the SDK:&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 @monocloud/auth-nextjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;.env.local&lt;/code&gt; with your credentials from the &lt;a href="https://dashboard.monocloud.com" rel="noopener noreferrer"&gt;MonoCloud dashboard&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;MONOCLOUD_AUTH_TENANT_DOMAIN=https://&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
MONOCLOUD_AUTH_APP_URL=http://localhost:3000
MONOCLOUD_AUTH_COOKIE_SECRET=&amp;lt;random-secret&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate a cookie secret with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl rand -hex 32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;In Next.js 16+, authentication middleware uses a proxy-based approach. Create &lt;code&gt;proxy.ts&lt;/code&gt; in your project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { authMiddleware } from "@monocloud/auth-nextjs";

export default authMiddleware();

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you the entire OpenID Connect flow — redirects, token exchange, cookie-based session storage, silent refresh. By default, every matched route requires authentication. Unauthenticated users are redirected to sign in.&lt;/p&gt;

&lt;p&gt;To protect only specific routes (everything else passes through unauthenticated):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { authMiddleware } from "@monocloud/auth-nextjs";

export default authMiddleware({
  protectedRoutes: ["/dashboard", "/settings", "/api/orders"],
});

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you need multiple configurations, custom routes, or dependency injection, you can create a &lt;code&gt;MonoCloudNextClient&lt;/code&gt; instance manually. For standard setups, the direct &lt;code&gt;authMiddleware()&lt;/code&gt; import above is all you need.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Protecting Pages
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;protectPage()&lt;/code&gt; is a HOC that wraps your Server Component. The user object is injected as a prop — you never need to call &lt;code&gt;getSession()&lt;/code&gt; manually inside a protected page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { MonoCloudUser, protectPage } from "@monocloud/auth-nextjs";

type Props = { user: MonoCloudUser };

function DashboardPage({ user }: Props) {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Welcome, {user.name}&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;{user.email}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default protectPage(DashboardPage);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unauthenticated users are redirected to sign in. Your component code only runs for authenticated sessions.&lt;/p&gt;

&lt;p&gt;To restrict to a specific group (RBAC):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default protectPage(AdminPage, { groups: ["admin"] });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users not in the &lt;code&gt;admin&lt;/code&gt; group are shown an access denied message before your component renders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protecting API Routes
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;protectApi()&lt;/code&gt; wraps your route handler. The handler signature is &lt;code&gt;(req, ctx)&lt;/code&gt; — to read the authenticated user inside, call &lt;code&gt;getSession()&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 { protectApi, getSession } from "@monocloud/auth-nextjs";
import { NextResponse } from "next/server";
import { db } from "@/lib/db";

export const GET = protectApi(async (req, ctx) =&amp;gt; {
  const session = await getSession();
  const orders = await db.orders.findMany({
    where: { userId: session!.user.sub },
  });
  return NextResponse.json(orders);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unauthenticated requests get a &lt;code&gt;401&lt;/code&gt; before your handler runs. &lt;code&gt;getSession()&lt;/code&gt; inside a protected handler is safe — the session is guaranteed to exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Actions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use server";

import { protect, getSession } from "@monocloud/auth-nextjs";
import { db } from "@/lib/db";

export async function placeOrder(items: CartItem[]) {
  await protect(); // redirects if not authenticated

  const session = await getSession();
  const userId = session!.user.sub;

  return db.orders.create({
    data: { userId, items, status: "pending" },
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;protect()&lt;/code&gt; guards the action — if the session is missing, the user is redirected to sign in and the action body never executes. Retrieve the user via &lt;code&gt;getSession()&lt;/code&gt; when you need identity data inside the action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading Auth State Server-Side
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { getSession } from "@monocloud/auth-nextjs";

export default async function HomePage() {
  const session = await getSession();

  if (!session) {
    return &amp;lt;p&amp;gt;You are not signed in.&amp;lt;/p&amp;gt;;
  }

  return &amp;lt;p&amp;gt;Hello, {session.user.name}&amp;lt;/p&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Client Components
&lt;/h2&gt;

&lt;p&gt;For client-side auth state, use &lt;code&gt;useAuth()&lt;/code&gt; from the &lt;code&gt;/client&lt;/code&gt; subpath:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import { useAuth } from "@monocloud/auth-nextjs/client";

export function UserMenu() {
  const { user, isAuthenticated, isLoading } = useAuth();

  if (isLoading) {
    return &amp;lt;div className="h-8 w-8 animate-pulse rounded-full bg-gray-200" /&amp;gt;;
  }

  if (!isAuthenticated || !user) {
    return &amp;lt;a href="/api/auth/login"&amp;gt;Sign in&amp;lt;/a&amp;gt;;
  }

  return (
    &amp;lt;div className="flex items-center gap-2"&amp;gt;
      &amp;lt;span className="text-sm"&amp;gt;{user.name}&amp;lt;/span&amp;gt;
      &amp;lt;a href="/api/auth/logout" className="text-sm text-gray-500"&amp;gt;Sign out&amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To protect a client component with a HOC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import { MonoCloudUser } from "@monocloud/auth-nextjs";
import { protectClientPage } from "@monocloud/auth-nextjs/client";

type Props = { user: MonoCloudUser };

const ProfilePage = ({ user }: Props) =&amp;gt; (
  &amp;lt;div&amp;gt;
    &amp;lt;h2&amp;gt;{user.name}&amp;lt;/h2&amp;gt;
    &amp;lt;p&amp;gt;{user.email}&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
);

export default protectClientPage(ProfilePage);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;code&gt;protectClientPage()&lt;/code&gt; runs in the browser — use &lt;code&gt;protectPage()&lt;/code&gt; (server) for anything security-sensitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sign In and Sign Out
&lt;/h2&gt;

&lt;p&gt;Use the &lt;code&gt;&amp;lt;SignIn /&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;SignOut /&amp;gt;&lt;/code&gt; components provided by the SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { getSession } from "@monocloud/auth-nextjs";
import { SignIn, SignOut } from "@monocloud/auth-nextjs/components";

export async function Navbar() {
  const session = await getSession();

  return (
    &amp;lt;nav&amp;gt;
      {session ? (
        &amp;lt;SignOut&amp;gt;Sign out&amp;lt;/SignOut&amp;gt;
      ) : (
        &amp;lt;SignIn&amp;gt;Sign in&amp;lt;/SignIn&amp;gt;
      )}
    &amp;lt;/nav&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK registers all required auth routes automatically. No route handler file needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Not to Do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't store tokens in localStorage.&lt;/strong&gt; Any XSS on your domain exposes them. The SDK uses httpOnly cookies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't check auth in client components for security.&lt;/strong&gt; Client-side auth checks are UI hints only. Use &lt;code&gt;protectPage()&lt;/code&gt; or &lt;code&gt;protectApi()&lt;/code&gt; for enforcement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't roll your own JWT validation.&lt;/strong&gt; JWKS rotation, clock skew, audience verification, algorithm pinning — there are eight ways to get this wrong and you'll find most of them in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://www.monocloud.com/docs/sdks/nextjs/index" rel="noopener noreferrer"&gt;MonoCloud Next.js SDK&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Working example:&lt;/strong&gt; &lt;a href="https://github.com/monocloud/monogrub-nextjs-demo" rel="noopener noreferrer"&gt;MonoGrub on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add RBAC:&lt;/strong&gt; Pass &lt;code&gt;groups: ["admin"]&lt;/code&gt; to &lt;code&gt;protectPage()&lt;/code&gt; or &lt;code&gt;protectApi()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profile management:&lt;/strong&gt; &lt;a href="https://github.com/monocloud/management-js" rel="noopener noreferrer"&gt;MonoCloud Management SDK&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start Building for Free with MonoCloud
&lt;/h2&gt;

&lt;p&gt;We built MonoCloud after learning for ourselves that authentication and user management are hard. But we knew that building an easy-to-use tool wasn't enough. Our platform isn't just about saving you a few days of coding. Instead, it's about adding the expertise you need to secure your software for the long term.&lt;/p&gt;

&lt;p&gt;You can start building with MonoCloud today. Sign up now for your free account. Click here &lt;a href="https://www.monocloud.com/" rel="noopener noreferrer"&gt;monocloud.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>authentication</category>
      <category>webdev</category>
      <category>oauth</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>
