<?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: Emaani Dev</title>
    <description>The latest articles on DEV Community by Emaani Dev (@emaanidev).</description>
    <link>https://dev.to/emaanidev</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%2F1893162%2F51d4cd8d-9f3b-4623-88d9-1cc6cee74329.jpg</url>
      <title>DEV Community: Emaani Dev</title>
      <link>https://dev.to/emaanidev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emaanidev"/>
    <language>en</language>
    <item>
      <title>Implementing Secure Social Login Authentication in Next.js 13+ NextAuth.js</title>
      <dc:creator>Emaani Dev</dc:creator>
      <pubDate>Thu, 16 Jan 2025 19:42:36 +0000</pubDate>
      <link>https://dev.to/emaanidev/implementing-secure-social-login-authentication-in-nextjs-13-with-nextauthjs-303d</link>
      <guid>https://dev.to/emaanidev/implementing-secure-social-login-authentication-in-nextjs-13-with-nextauthjs-303d</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Social login authentication has become a standard feature in modern web applications. This article will guide you through implementing secure social login using Next.js 13+, NextAuth.js, and Prisma, with Google and Facebook as authentication providers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 16+&lt;/li&gt;
&lt;li&gt;Next.js 13+&lt;/li&gt;
&lt;li&gt;PostgreSQL database&lt;/li&gt;
&lt;li&gt;Google and Facebook Developer accounts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Initial Setup&lt;/li&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;li&gt;Database Integration&lt;/li&gt;
&lt;li&gt;Implementation&lt;/li&gt;
&lt;li&gt;Error Handling&lt;/li&gt;
&lt;li&gt;Security Considerations&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Best Practices&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Initial Setup
&lt;/h2&gt;

&lt;p&gt;First, install the required dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;next-auth @auth/prisma-adapter prisma @prisma/client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Configuration
&lt;/h2&gt;

&lt;p&gt;Environment Variables&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.env&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;# OAuth Configuration
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
FACEBOOK_CLIENT_ID=your_facebook_client_id
FACEBOOK_CLIENT_SECRET=your_facebook_client_secret
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your_secure_secret

# Database
DATABASE_URL="postgresql://user:password@localhost:5432/dbname"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NextAuth Configuration&lt;/p&gt;

&lt;p&gt;Create the authentication configuration 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 NextAuth, { DefaultSession, NextAuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import FacebookProvider from "next-auth/providers/facebook";
import { PrismaAdapter } from "@auth/prisma-adapter";
import prisma from "@/app/lib/prisma";

declare module "next-auth" {
  interface Session extends DefaultSession {
    user: {
      id: string;
      email: string;
      name?: string | null;
    } &amp;amp; DefaultSession["user"]
  }
}

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prisma),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID ?? "",
      clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
    }),
    FacebookProvider({
      clientId: process.env.FACEBOOK_CLIENT_ID ?? "",
      clientSecret: process.env.FACEBOOK_CLIENT_SECRET ?? "",
    }),
  ],
  callbacks: {
    async session({ session, user }) {
      if (session.user) {
        session.user.id = user.id;
      }
      return session;
    },
    async signIn({ user, account, profile }) {
      try {
        if (!user.email) return false;

        const existingUser = await prisma.user.findUnique({
          where: { email: user.email },
          include: { profile: true },
        });

        if (!existingUser) {
          await prisma.user.create({
            data: {
              email: user.email,
              name: user.name || "",
              profile: {
                create: {
                  firstName: (profile as any)?.given_name || "",
                  lastName: (profile as any)?.family_name || "",
                }
              }
            },
          });
        }
        return true;
      } catch (error) {
        console.error("Error in signIn callback:", error);
        return false;
      }
    },
  },
  pages: {
    signIn: '/login',
    error: '/auth/error',
  },
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Database Integration
&lt;/h2&gt;

&lt;p&gt;Prisma Schema&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model User {
  id            String    @id @default(cuid())
  email         String    @unique
  name          String?
  profile       Profile?
  accounts      Account[]
  sessions      Session[]
}

model Profile {
  id            String    @id @default(cuid())
  userId        String    @unique
  firstName     String
  lastName      String
  user          User      @relation(fields: [userId], references: [id])
}

// NextAuth.js required models
model Account {
  id                 String  @id @default(cuid())
  userId             String
  type               String
  provider           String
  providerAccountId  String
  refresh_token      String?  @db.Text
  access_token       String?  @db.Text
  expires_at         Int?
  token_type         String?
  scope              String?
  id_token           String?  @db.Text
  session_state      String?

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Implementation
&lt;/h2&gt;

&lt;p&gt;Authentication&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 { SessionProvider } from "next-auth/react";

export function AuthProvider({ children }: { children: React.ReactNode }) {
  return &amp;lt;SessionProvider&amp;gt;{children}&amp;lt;/SessionProvider&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 Social Login Buttons Component
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";
import { signIn } from "next-auth/react";
import { FaGoogle, FaFacebook } from "react-icons/fa";

export const SocialLoginButtons = () =&amp;gt; {
  const handleSocialLogin = async (provider: "google" | "facebook") =&amp;gt; {
    try {
      const result = await signIn(provider, {
        callbackUrl: '/dashboard',
        redirect: false,
      });

      if (result?.error) {
        console.error('Social login error:', result.error);
      }
    } catch (error) {
      console.error(`${provider} login error:`, error);
    }
  };

  return (
    &amp;lt;div className="space-y-4"&amp;gt;
      &amp;lt;button
        onClick={() =&amp;gt; handleSocialLogin("google")}
        className="w-full flex items-center justify-center gap-2 bg-white text-gray-700 border border-gray-300 rounded-lg px-4 py-2 hover:bg-gray-50"
      &amp;gt;
        &amp;lt;FaGoogle className="text-red-500" /&amp;gt;
        Continue with Google
      &amp;lt;/button&amp;gt;
      &amp;lt;button
        onClick={() =&amp;gt; handleSocialLogin("facebook")}
        className="w-full flex items-center justify-center gap-2 bg-blue-600 text-white rounded-lg px-4 py-2 hover:bg-blue-700"
      &amp;gt;
        &amp;lt;FaFacebook /&amp;gt;
        Continue with Facebook
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Error Handling
&lt;/h2&gt;

&lt;p&gt;Create a custom error page:&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 { useSearchParams } from "next/navigation";

export default function AuthError() {
  const searchParams = useSearchParams();
  const error = searchParams.get("error");

  return (
    &amp;lt;div className="min-h-screen flex items-center justify-center"&amp;gt;
      &amp;lt;div className="bg-white p-8 rounded-lg shadow-md"&amp;gt;
        &amp;lt;h1 className="text-2xl font-bold text-red-600 mb-4"&amp;gt;
          Authentication Error
        &amp;lt;/h1&amp;gt;
        &amp;lt;p className="text-gray-600"&amp;gt;
          {error || "An error occurred during authentication"}
        &amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Security Considerations
&lt;/h2&gt;

&lt;p&gt;CORS Configuration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/api/')) {
    const response = NextResponse.next();
    response.headers.set('Access-Control-Allow-Credentials', 'true');
    response.headers.set('Access-Control-Allow-Origin', '*');
    response.headers.set('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
    response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    return response;
  }
  return NextResponse.next();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Testing
&lt;/h2&gt;

&lt;p&gt;Test Authentication Flow&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { render, fireEvent, waitFor } from '@testing-library/react';
import { SocialLoginButtons } from '@/components/SocialLoginButtons';
import { signIn } from 'next-auth/react';

jest.mock('next-auth/react');

describe('SocialLoginButtons', () =&amp;gt; {
  it('handles Google login correctly', async () =&amp;gt; {
    const { getByText } = render(&amp;lt;SocialLoginButtons /&amp;gt;);
    const googleButton = getByText('Continue with Google');

    fireEvent.click(googleButton);

    await waitFor(() =&amp;gt; {
      expect(signIn).toHaveBeenCalledWith('google', {
        callbackUrl: '/dashboard',
        redirect: false,
      });
    });
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Environment Variables&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never commit sensitive credentials&lt;/li&gt;
&lt;li&gt;Use different OAuth credentials for development and production&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement comprehensive error logging&lt;/li&gt;
&lt;li&gt;Provide user-friendly error messages&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement rate limiting&lt;/li&gt;
&lt;li&gt;Use HTTPS in production&lt;/li&gt;
&lt;li&gt;Keep dependencies updated&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User Experience&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add loading states&lt;/li&gt;
&lt;li&gt;Provide clear feedback&lt;/li&gt;
&lt;li&gt;Handle offline scenarios&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;This implementation provides a secure and user-friendly social login system. Remember to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regularly update dependencies&lt;/li&gt;
&lt;li&gt;Monitor authentication logs&lt;/li&gt;
&lt;li&gt;Test thoroughly across different browsers&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle edge cases appropriately&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://nextjs.org/docs" rel="noopener noreferrer"&gt;Next.js Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://next-auth.js.org" rel="noopener noreferrer"&gt;NextAuth.js Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://console.cloud.google.com" rel="noopener noreferrer"&gt;Google OAuth Setup Guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developers.facebook.com" rel="noopener noreferrer"&gt;Facebook OAuth Setup Guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article provides a solid foundation for implementing social login in your Next.js application. For production deployment, ensure you follow security best practices and thoroughly test the implementation.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Bulletproof JWT Authentication: Essential Security Patterns for Production Apps</title>
      <dc:creator>Emaani Dev</dc:creator>
      <pubDate>Fri, 03 Jan 2025 18:12:57 +0000</pubDate>
      <link>https://dev.to/emaanidev/bulletproof-jwt-authentication-essential-security-patterns-for-production-apps-4c7g</link>
      <guid>https://dev.to/emaanidev/bulletproof-jwt-authentication-essential-security-patterns-for-production-apps-4c7g</guid>
      <description>&lt;p&gt;JSON Web Tokens (JWT) authentication has become a cornerstone of modern web applications due to its simplicity and scalability. However, implementing JWT securely requires adherence to best practices. Here's a guide to help you protect your applications effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Security Principles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Token Structure and Validation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use Strong Signing Algorithms&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opt for asymmetric algorithms like RS256 over symmetric ones like HS256 for enhanced security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implement Strict Payload Validation&lt;/p&gt;

&lt;p&gt;Validate every claim in the token to ensure integrity.&lt;/p&gt;

&lt;p&gt;Include Essential Claims&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Incorporate claims like iat (issued at), exp (expiration), aud (audience), iss (issuer), and sub (subject) to strengthen token purpose and validity.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const token = jwt.sign(
  {
    sub: user.id,
    iss: 'your-app-name',
    aud: 'your-api',
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour
  },
  privateKey,
  { algorithm: 'RS256' }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Storage and Transmission&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use HttpOnly Cookies&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store tokens in HttpOnly cookies to prevent client-side JavaScript access, mitigating XSS risks.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;res.cookie('access_token', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 3600000, // 1 hour
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable Secure Flags.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure secure and sameSite attributes are enabled to reduce exposure during token transmission.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Pitfalls to Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Token Invalidation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Blacklist Revoked Tokens.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintain a server-side token blacklist to invalidate compromised tokens efficiently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use Short Expiration Times.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pair short-lived access tokens with refresh tokens for better control over session lifecycles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;XSS and CSRF Protection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Avoid Storing JWTs in LocalStorage.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LocalStorage is susceptible to XSS attacks. Prefer HttpOnly cookies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use CSRF Tokens.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For state-changing operations, implement CSRF tokens to prevent cross-site request forgery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enable Content Security Policy (CSP)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set CSP headers to block malicious scripts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Secret Management&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use Asymmetric Keys.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rely on public/private key pairs for signing and verifying tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rotate Keys Regularly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regularly update keys to limit exposure in case of compromise.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid Hardcoding Secrets.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use environment variables or secret management tools, and keep sensitive data out of version control.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Best Practices for Production&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Middleware for Token Verification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Implement middleware to verify tokens and enforce authentication securely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const verifyToken = async (req, res, next) =&amp;gt; {
  try {
    const token = req.cookies.access_token;
    const decoded = await jwt.verify(token, publicKey, {
      algorithms: ['RS256'],
      issuer: 'your-app-name',
      audience: 'your-api',
    });

    if (await isTokenBlacklisted(token)) {
      throw new Error('Token revoked');
    }

    req.user = decoded;
    next();
  } catch (err) {
    res.status(401).json({ error: 'Authentication failed' });
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Layered Security&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;JWT authentication is just one aspect of a robust security strategy. Complement it with:&lt;/p&gt;

&lt;p&gt;Rate Limiting&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mitigate brute-force attacks by limiting the number of requests per user/IP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Proper Error Handling&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid exposing sensitive error details in responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regular Security Audits&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conduct periodic reviews and penetration testing to identify vulnerabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these practices, you can build secure, scalable, and resilient web authentication systems. &lt;strong&gt;Remember, security is a journey, not a destination.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>authentication</category>
      <category>node</category>
    </item>
  </channel>
</rss>
