DEV Community

Cover image for Authentication Service Types
Taki
Taki

Posted on

Authentication Service Types

Here’s a comprehensive list of authentication service types (both strategies and mechanisms), with a brief explanation and common use cases — especially helpful for backend apps like those built with NestJS, and frontend integrations via Next.js:


🔐 Common Authentication Strategies

Strategy Description Use Case Example
Session-based Auth Stores session ID on server and client via cookies. Server holds the session state. Classic web apps (e.g., Express, Rails)
JWT (JSON Web Token) Stateless, token-based. Token is signed and sent in headers (usually Authorization). Modern SPAs, mobile apps
OAuth 2.0 Delegated auth using third-party providers like Google, Facebook, GitHub. “Login with Google”
OpenID Connect (OIDC) Built on top of OAuth 2.0, adds identity layer. Returns id_token. Auth0, Azure AD
API Key Auth Simple token passed via headers or query params. Internal services, public APIs
Basic Auth Username and password encoded in base64 in the Authorization header. Legacy or internal APIs
Bearer Token Generic token-based auth (includes JWT, API keys, etc.). REST APIs
Mutual TLS (mTLS) Both client and server verify each other's certificates. High-security enterprise APIs
HMAC (Hash-based Message Authentication Code) Used to sign messages with a shared secret. Webhooks, AWS API Gateway
SAML (Security Assertion Markup Language) XML-based auth, often used in enterprise SSO. Corporate portals
Magic Link / Email Link Auth Auth via secure, expiring link sent to email. No password needed. Passwordless login
One-Time Password (OTP) Temporary code sent via email/SMS or generated by app. MFA (2FA) flows
Social Auth Use third-party providers for identity (Google, Facebook, Apple, etc). User convenience
Fingerprint / Biometrics Device-native auth via fingerprint, FaceID. Mobile/web PWA apps
Device/Token Binding Ties auth token to device or browser instance. Secure mobile or banking apps

🧱 Storage Mechanisms for Auth State

Storage Location Used By Consideration
HTTP-Only Cookies Session-based, JWT Secure, not accessible by JS
LocalStorage JWT (client-side) Vulnerable to XSS
SessionStorage JWT (session-level) Cleared on tab close
In-memory (volatile) Mobile apps, SPAs Lost on refresh
Redis / DB Session Store Session-based auth Good for scaling with session persistence

Advanced Concepts

  • Refresh Tokens: Used with JWT to rotate tokens without forcing re-login.
  • Multi-Factor Authentication (MFA): OTP, email, biometrics, etc.
  • Role-Based Access Control (RBAC): Authorization layer (not auth but closely tied).
  • Attribute-Based Access Control (ABAC): Access based on attributes/claims.
  • Token Introspection / Revocation: OAuth feature to validate token state dynamically.

🛠️ Popular Services/Providers

Provider Type Notes
Auth0 OAuth2/OIDC, SAML, JWT Full-service auth platform
Firebase Auth JWT, OAuth2 Easy for mobile/web apps
Clerk OIDC, Magic Links Dev-first auth-as-a-service
Supabase Auth JWT, OAuth2 Open-source Firebase alternative
Okta OAuth2, SAML, OIDC Enterprise identity management
Keycloak OAuth2, SAML, OpenID Self-hosted, flexible
NextAuth.js Session/JWT/OAuth2 Next.js-friendly

Sure, Taki. Here’s a slow, step-by-step breakdown of how to architect a robust authentication system using NestJS (backend) and Next.js (frontend) — covering Session-based, JWT-based, and OAuth2 (with providers like Google) patterns.


🧭 Overview: Auth Patterns

I'll cover 3 patterns with full architecture and how they integrate:

  1. Session-based Auth – Secure, cookie-stored sessions with a backend session store (e.g., Redis).
  2. JWT-based Auth (Access + Refresh Tokens) – Stateless, token-based with refresh flow.
  3. OAuth2/OIDC Login – Login with Google/GitHub using OAuth2.

Let’s go deep — starting with Pattern #1.


🔐 PATTERN 1: SESSION-BASED AUTH (NestJS + Next.js)

✅ When to Use:

  • You need HTTP-only cookies.
  • CSRF protection is important.
  • You want the server to manage session lifecycle (central logout, etc.).
  • Excellent for SSR with Next.js.

⚙️ High-Level Architecture:

[ Next.js Client ] <-> [ NestJS Backend (Express adapter) + Session Store (Redis) ]
          |
     Cookies (HttpOnly, Secure)
          |
[ Auth Session stored on server (via session ID) ]
Enter fullscreen mode Exit fullscreen mode

🧱 Step-by-Step Implementation

1. 📦 NestJS Backend Setup

a. Install dependencies:

npm install @nestjs/passport passport passport-local express-session connect-redis ioredis
npm install @nestjs/platform-express
Enter fullscreen mode Exit fullscreen mode

b. Enable Express Session (in main.ts):

import * as session from 'express-session';
import * as connectRedis from 'connect-redis';
import { createClient } from 'redis';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const RedisStore = connectRedis(session);
  const redisClient = createClient();

  app.use(
    session({
      store: new RedisStore({ client: redisClient }),
      secret: 'your-secret',
      resave: false,
      saveUninitialized: false,
      cookie: {
        httpOnly: true,
        secure: true, // true in production
        maxAge: 1000 * 60 * 60, // 1 hour
      },
    }),
  );

  await app.listen(3001);
}
Enter fullscreen mode Exit fullscreen mode

c. Set up Local Auth (Passport):

  • local.strategy.ts:
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super(); // uses username/password fields by default
  }

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) throw new UnauthorizedException();
    return user;
  }
}
Enter fullscreen mode Exit fullscreen mode
  • auth.controller.ts:
@UseGuards(AuthGuard('local'))
@Post('login')
login(@Request() req): any {
  return req.user; // user info is now in session
}

@Get('profile')
getProfile(@Request() req) {
  return req.user; // automatically loaded from session
}
Enter fullscreen mode Exit fullscreen mode
  • auth.service.ts:
@Injectable()
export class AuthService {
  async validateUser(username: string, password: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user && user.password === password) {
      const { password, ...rest } = user;
      return rest;
    }
    return null;
  }
}
Enter fullscreen mode Exit fullscreen mode

2. 🧼 Next.js Frontend Setup

a. Use fetch with credentials: 'include'

// login.ts
const login = async (username: string, password: string) => {
  const res = await fetch('http://localhost:3001/auth/login', {
    method: 'POST',
    credentials: 'include', // important for cookies
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ username, password }),
  });

  const data = await res.json();
  return data;
};
Enter fullscreen mode Exit fullscreen mode

b. Use getServerSideProps with cookie

You can access the session via cookie in SSR.


3. 🔒 Secure It for Production

  • Use HTTPS (for secure: true cookie).
  • Use CSRF protection if modifying server state.
  • Store session secrets securely (e.g., .env + vault).

✅ Pros & Cons

✅ Pros ❌ Cons
Works well with SSR Less scalable (stateful)
HttpOnly cookies (XSS-safe) Redis or DB session storage
Central logout & session control CSRF concerns

✅ Ready to go stateless? Let’s move on to Pattern 2: JWT Auth.


🔐 PATTERN 2: JWT-BASED AUTH (Access + Refresh)

✅ When to Use:

  • Mobile or SPAs (Next.js API routes, no SSR)
  • Stateless services (microservices)
  • You want scalability without session stores

⚙️ Architecture:

[ Next.js Client ] ⇄ [ NestJS Auth API ]
   |                         |
 LocalStorage           JWT Access Token (short-lived)
 Refresh Token (Cookie or DB) ⇄ /auth/refresh
Enter fullscreen mode Exit fullscreen mode

🧱 Implementation Steps

1. 📦 NestJS Backend Setup

a. Install packages

npm install @nestjs/jwt passport-jwt
Enter fullscreen mode Exit fullscreen mode

b. Configure JWT strategy

  • jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: 'jwt-secret',
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}
Enter fullscreen mode Exit fullscreen mode

c. Issue JWTs

  • auth.service.ts
async login(user: any) {
  const payload = { username: user.username, sub: user.id };
  const accessToken = this.jwtService.sign(payload, { expiresIn: '15m' });
  const refreshToken = this.jwtService.sign(payload, { expiresIn: '7d' });
  return { accessToken, refreshToken };
}
Enter fullscreen mode Exit fullscreen mode
  • Store refresh token in DB or send it in secure HTTP-only cookie.

2. 🔄 Refresh Token Endpoint

@Post('refresh')
refresh(@Body() body) {
  const valid = this.jwtService.verify(body.refreshToken);
  const accessToken = this.jwtService.sign({ ...valid }, { expiresIn: '15m' });
  return { accessToken };
}
Enter fullscreen mode Exit fullscreen mode

3. 🧼 Next.js Frontend Setup

  • Store accessToken in memory or localStorage
  • Auto-attach Authorization: Bearer <token> to each request
  • On 401 → trigger /auth/refresh

✅ Pros & Cons

✅ Pros ❌ Cons
Stateless (scales easily) Refresh token complexity
Works well on mobile Vulnerable if token leaked
Easy to implement Needs secure token rotation

Up next: Pattern 3 – OAuth2 / Google Login (e.g., "Sign in with Google")


🌐 PATTERN 3: OAuth2 (Login with Google) – NestJS + Next.js


✅ When to Use

✅ Ideal For
User-friendly social login
Offloading identity verification to Google, etc
Supporting SSO in enterprise (via OIDC)
Not storing passwords in your DB

⚙️ Architecture

[ Next.js Client ]
      ↓ click
[ Google Login Button ] 
      ↓ redirect
[ NestJS OAuth Controller ]
      → [ Google Auth ]
      ← Access Token + Profile
      → Issue JWT / Session internally
Enter fullscreen mode Exit fullscreen mode

📌 Goals

  • Redirect user to Google OAuth
  • Receive Google user profile
  • Create or find user in DB
  • Return your own JWT or session cookie to frontend

🧱 Step-by-Step: NestJS Backend


1. 📦 Install Required Packages

npm install passport passport-google-oauth20 @nestjs/passport
Enter fullscreen mode Exit fullscreen mode

2. 📁 Create Google Strategy

google.strategy.ts

import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { Strategy, VerifyCallback } from 'passport-google-oauth20';

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
  constructor() {
    super({
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: 'http://localhost:3001/auth/google/redirect',
      scope: ['email', 'profile'],
    });
  }

  async validate(accessToken: string, refreshToken: string, profile: any, done: VerifyCallback): Promise<any> {
    const { name, emails, photos } = profile;
    const user = {
      email: emails[0].value,
      firstName: name.givenName,
      lastName: name.familyName,
      picture: photos[0].value,
      accessToken,
    };
    done(null, user);
  }
}
Enter fullscreen mode Exit fullscreen mode

3. 📁 Auth Controller for Google

auth.controller.ts

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @Get('google')
  @UseGuards(AuthGuard('google'))
  async googleLogin() {
    // Redirects to Google
  }

  @Get('google/redirect')
  @UseGuards(AuthGuard('google'))
  async googleRedirect(@Req() req: Request, @Res() res: Response) {
    const user = req.user; // Google profile
    const jwt = await this.authService.googleLogin(req.user); // Issue JWT

    res.cookie('jwt', jwt, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
    });

    return res.redirect('http://localhost:3000/dashboard');
  }
}
Enter fullscreen mode Exit fullscreen mode

4. 🧠 Auth Service Logic

auth.service.ts

async googleLogin(googleUser: any): Promise<string> {
  const user = await this.usersService.findOrCreate(googleUser.email, googleUser);
  const payload = { sub: user.id, email: user.email };
  return this.jwtService.sign(payload);
}
Enter fullscreen mode Exit fullscreen mode

5. 🧩 Plug in GoogleStrategy

auth.module.ts

@Module({
  providers: [AuthService, GoogleStrategy],
  controllers: [AuthController],
})
export class AuthModule {}
Enter fullscreen mode Exit fullscreen mode

🧼 Next.js Frontend


1. 🔘 Login Button

// /login.tsx
const handleGoogleLogin = () => {
  window.location.href = 'http://localhost:3001/auth/google';
};
Enter fullscreen mode Exit fullscreen mode

2. 🔐 Protected Pages (SSR / Client)

Client-side (token from cookie):

  • You can use getServerSideProps and read cookie to validate user.
  • OR store JWT from backend in localStorage and attach manually.

✅ Summary: Pros & Cons

✅ Pros ❌ Cons
No password management Requires setting up OAuth apps
Trust Google/Facebook/etc Depends on 3rd party availability
Easy login UX May not work for internal users
Works with JWT or Session backend Token exchange complexity

🔒 Security Tips

  • Always use https + httpOnly cookies if possible
  • Handle token expiration
  • Optional: bind session to IP/device
  • Optional: add MFA after social login

🧪 Optional Enhancements

  • Add GitHub / Facebook / Microsoft login by replicating strategy
  • Support OIDC Enterprise providers (e.g., Azure AD)
  • Store refresh token from Google if you want to call Google APIs
  • Combine with role-based access

Top comments (0)