Implement Deno JWT authentication with OAuth2 integration for secure API development

Intermediate 45 min Apr 09, 2026 11 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up production-grade JWT authentication middleware in Deno with OAuth2 provider integration and role-based access control. Learn to secure API endpoints with proper token validation, user management, and enterprise-ready authentication flows.

Prerequisites

  • Deno runtime installed
  • Basic TypeScript knowledge
  • Understanding of JWT concepts
  • OAuth2 provider applications (Google/GitHub)

What this solves

Modern web applications require secure authentication mechanisms that can scale with growing user bases and integrate with existing identity providers. This tutorial shows you how to implement JWT (JSON Web Token) authentication in Deno applications with OAuth2 integration, enabling secure API development with role-based access control and session management.

Step-by-step installation

Install Deno runtime

Install the latest Deno runtime with security features enabled. Deno provides built-in support for modern web standards including JWT handling.

curl -fsSL https://deno.land/install.sh | sh
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
curl -fsSL https://deno.land/install.sh | sh
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Create project structure

Set up the directory structure for your Deno JWT authentication project with proper organization for middleware, routes, and configuration.

mkdir -p ~/deno-jwt-auth/{middleware,routes,config,utils,types}
cd ~/deno-jwt-auth

Create JWT configuration

Configure JWT settings including secret keys, expiration times, and OAuth2 provider settings. Use environment variables for sensitive configuration.

export interface AuthConfig {
  jwtSecret: string;
  jwtExpiration: string;
  refreshTokenExpiration: string;
  oauth2: {
    google: {
      clientId: string;
      clientSecret: string;
      redirectUri: string;
    };
    github: {
      clientId: string;
      clientSecret: string;
      redirectUri: string;
    };
  };
}

export const authConfig: AuthConfig = {
  jwtSecret: Deno.env.get("JWT_SECRET") || "your-super-secret-jwt-key-change-in-production",
  jwtExpiration: "1h",
  refreshTokenExpiration: "7d",
  oauth2: {
    google: {
      clientId: Deno.env.get("GOOGLE_CLIENT_ID") || "",
      clientSecret: Deno.env.get("GOOGLE_CLIENT_SECRET") || "",
      redirectUri: Deno.env.get("GOOGLE_REDIRECT_URI") || "http://localhost:8000/auth/google/callback"
    },
    github: {
      clientId: Deno.env.get("GITHUB_CLIENT_ID") || "",
      clientSecret: Deno.env.get("GITHUB_CLIENT_SECRET") || "",
      redirectUri: Deno.env.get("GITHUB_REDIRECT_URI") || "http://localhost:8000/auth/github/callback"
    }
  }
};

Define TypeScript interfaces

Create type definitions for users, JWT payloads, and OAuth2 responses to ensure type safety throughout your application.

export interface User {
  id: string;
  email: string;
  name: string;
  roles: string[];
  provider: 'local' | 'google' | 'github';
  providerId?: string;
  createdAt: Date;
  lastLogin?: Date;
}

export interface JWTPayload {
  sub: string; // user id
  email: string;
  name: string;
  roles: string[];
  iat: number;
  exp: number;
}

export interface RefreshToken {
  token: string;
  userId: string;
  expiresAt: Date;
  createdAt: Date;
}

export interface OAuth2UserInfo {
  id: string;
  email: string;
  name: string;
  picture?: string;
}

export interface AuthResponse {
  accessToken: string;
  refreshToken: string;
  user: User;
}

Create JWT utilities

Implement JWT token generation, validation, and refresh functionality with proper error handling and security measures.

import { create, verify, decode } from "https://deno.land/x/djwt@v3.0.2/mod.ts";
import { authConfig } from "../config/auth.ts";
import type { JWTPayload, User } from "../types/auth.ts";

const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
  "raw",
  encoder.encode(authConfig.jwtSecret),
  { name: "HMAC", hash: "SHA-256" },
  false,
  ["sign", "verify"]
);

export async function generateAccessToken(user: User): Promise {
  const payload: JWTPayload = {
    sub: user.id,
    email: user.email,
    name: user.name,
    roles: user.roles,
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1 hour
  };

  return await create({ alg: "HS256", typ: "JWT" }, payload, key);
}

export async function generateRefreshToken(): Promise {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}

export async function verifyAccessToken(token: string): Promise {
  try {
    const payload = await verify(token, key);
    return payload as JWTPayload;
  } catch (error) {
    console.error("JWT verification failed:", error.message);
    return null;
  }
}

export function decodeToken(token: string): JWTPayload | null {
  try {
    const [header, payload] = decode(token);
    return payload as JWTPayload;
  } catch {
    return null;
  }
}

Implement OAuth2 providers

Create OAuth2 integration handlers for Google and GitHub authentication with proper token exchange and user information retrieval.

import { authConfig } from "../config/auth.ts";
import type { OAuth2UserInfo } from "../types/auth.ts";

export class OAuth2Provider {
  static getGoogleAuthUrl(state: string): string {
    const params = new URLSearchParams({
      client_id: authConfig.oauth2.google.clientId,
      redirect_uri: authConfig.oauth2.google.redirectUri,
      response_type: "code",
      scope: "openid email profile",
      state: state
    });
    return https://accounts.google.com/o/oauth2/v2/auth?${params.toString()};
  }

  static getGithubAuthUrl(state: string): string {
    const params = new URLSearchParams({
      client_id: authConfig.oauth2.github.clientId,
      redirect_uri: authConfig.oauth2.github.redirectUri,
      scope: "user:email",
      state: state
    });
    return https://github.com/login/oauth/authorize?${params.toString()};
  }

  static async exchangeGoogleCode(code: string): Promise {
    try {
      // Exchange code for access token
      const tokenResponse = await fetch("https://oauth2.googleapis.com/token", {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: new URLSearchParams({
          client_id: authConfig.oauth2.google.clientId,
          client_secret: authConfig.oauth2.google.clientSecret,
          code: code,
          grant_type: "authorization_code",
          redirect_uri: authConfig.oauth2.google.redirectUri
        })
      });

      const tokenData = await tokenResponse.json();
      if (!tokenData.access_token) return null;

      // Get user information
      const userResponse = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
        headers: { "Authorization": Bearer ${tokenData.access_token} }
      });

      const userData = await userResponse.json();
      return {
        id: userData.id,
        email: userData.email,
        name: userData.name,
        picture: userData.picture
      };
    } catch (error) {
      console.error("Google OAuth2 error:", error);
      return null;
    }
  }

  static async exchangeGithubCode(code: string): Promise {
    try {
      // Exchange code for access token
      const tokenResponse = await fetch("https://github.com/login/oauth/access_token", {
        method: "POST",
        headers: {
          "Accept": "application/json",
          "Content-Type": "application/x-www-form-urlencoded"
        },
        body: new URLSearchParams({
          client_id: authConfig.oauth2.github.clientId,
          client_secret: authConfig.oauth2.github.clientSecret,
          code: code
        })
      });

      const tokenData = await tokenResponse.json();
      if (!tokenData.access_token) return null;

      // Get user information
      const userResponse = await fetch("https://api.github.com/user", {
        headers: { "Authorization": Bearer ${tokenData.access_token} }
      });

      const userData = await userResponse.json();
      return {
        id: userData.id.toString(),
        email: userData.email,
        name: userData.name || userData.login
      };
    } catch (error) {
      console.error("GitHub OAuth2 error:", error);
      return null;
    }
  }
}

Create JWT authentication middleware

Implement middleware to validate JWT tokens, extract user information, and enforce authentication requirements on protected routes.

import type { Context, Next } from "https://deno.land/x/oak@v15.0.0/mod.ts";
import { verifyAccessToken } from "../utils/jwt.ts";
import type { JWTPayload } from "../types/auth.ts";

declare module "https://deno.land/x/oak@v15.0.0/mod.ts" {
  interface Context {
    user?: JWTPayload;
  }
}

export async function authenticateToken(ctx: Context, next: Next) {
  const authHeader = ctx.request.headers.get("Authorization");
  const token = authHeader?.split(" ")[1]; // Bearer TOKEN

  if (!token) {
    ctx.response.status = 401;
    ctx.response.body = { error: "Access token required" };
    return;
  }

  const payload = await verifyAccessToken(token);
  if (!payload) {
    ctx.response.status = 401;
    ctx.response.body = { error: "Invalid or expired token" };
    return;
  }

  // Check token expiration
  if (payload.exp < Math.floor(Date.now() / 1000)) {
    ctx.response.status = 401;
    ctx.response.body = { error: "Token expired" };
    return;
  }

  ctx.user = payload;
  await next();
}

export function requireRoles(...roles: string[]) {
  return async (ctx: Context, next: Next) => {
    if (!ctx.user) {
      ctx.response.status = 401;
      ctx.response.body = { error: "Authentication required" };
      return;
    }

    const hasRole = roles.some(role => ctx.user!.roles.includes(role));
    if (!hasRole) {
      ctx.response.status = 403;
      ctx.response.body = { error: "Insufficient permissions" };
      return;
    }

    await next();
  };
}

export async function optionalAuth(ctx: Context, next: Next) {
  const authHeader = ctx.request.headers.get("Authorization");
  const token = authHeader?.split(" ")[1];

  if (token) {
    const payload = await verifyAccessToken(token);
    if (payload && payload.exp >= Math.floor(Date.now() / 1000)) {
      ctx.user = payload;
    }
  }

  await next();
}

Create user management utilities

Implement user storage, retrieval, and management functions. This example uses in-memory storage, but you should replace with a database in production.

import type { User, RefreshToken } from "../types/auth.ts";

// In-memory storage (replace with database in production)
const users = new Map();
const refreshTokens = new Map();

export class UserService {
  static async createUser(userData: Omit): Promise {
    const user: User = {
      id: crypto.randomUUID(),
      ...userData,
      createdAt: new Date()
    };
    users.set(user.id, user);
    return user;
  }

  static async findById(id: string): Promise {
    return users.get(id) || null;
  }

  static async findByEmail(email: string): Promise {
    for (const user of users.values()) {
      if (user.email === email) {
        return user;
      }
    }
    return null;
  }

  static async findByProvider(provider: string, providerId: string): Promise {
    for (const user of users.values()) {
      if (user.provider === provider && user.providerId === providerId) {
        return user;
      }
    }
    return null;
  }

  static async updateLastLogin(userId: string): Promise {
    const user = users.get(userId);
    if (user) {
      user.lastLogin = new Date();
      users.set(userId, user);
    }
  }

  static async storeRefreshToken(token: string, userId: string): Promise {
    const refreshToken: RefreshToken = {
      token,
      userId,
      expiresAt: new Date(Date.now() + 7  24  60  60  1000), // 7 days
      createdAt: new Date()
    };
    refreshTokens.set(token, refreshToken);
  }

  static async validateRefreshToken(token: string): Promise {
    const refreshToken = refreshTokens.get(token);
    if (!refreshToken || refreshToken.expiresAt < new Date()) {
      if (refreshToken) {
        refreshTokens.delete(token);
      }
      return null;
    }
    return refreshToken.userId;
  }

  static async revokeRefreshToken(token: string): Promise {
    refreshTokens.delete(token);
  }

  static async revokeAllUserTokens(userId: string): Promise {
    for (const [token, refreshToken] of refreshTokens.entries()) {
      if (refreshToken.userId === userId) {
        refreshTokens.delete(token);
      }
    }
  }
}

Create authentication routes

Implement API endpoints for OAuth2 login, token refresh, logout, and user profile management with proper error handling.

import { Router } from "https://deno.land/x/oak@v15.0.0/mod.ts";
import { OAuth2Provider } from "../utils/oauth2.ts";
import { generateAccessToken, generateRefreshToken } from "../utils/jwt.ts";
import { UserService } from "../utils/users.ts";
import { authenticateToken } from "../middleware/auth.ts";
import type { AuthResponse } from "../types/auth.ts";

const router = new Router({ prefix: "/auth" });

// Generate OAuth2 authorization URLs
router.get("/google", (ctx) => {
  const state = crypto.randomUUID();
  // Store state in session or cache for validation
  const authUrl = OAuth2Provider.getGoogleAuthUrl(state);
  ctx.response.body = { authUrl, state };
});

router.get("/github", (ctx) => {
  const state = crypto.randomUUID();
  const authUrl = OAuth2Provider.getGithubAuthUrl(state);
  ctx.response.body = { authUrl, state };
});

// OAuth2 callback handlers
router.get("/google/callback", async (ctx) => {
  const code = ctx.request.url.searchParams.get("code");
  const state = ctx.request.url.searchParams.get("state");

  if (!code) {
    ctx.response.status = 400;
    ctx.response.body = { error: "Authorization code required" };
    return;
  }

  const userInfo = await OAuth2Provider.exchangeGoogleCode(code);
  if (!userInfo) {
    ctx.response.status = 400;
    ctx.response.body = { error: "Failed to get user information" };
    return;
  }

  let user = await UserService.findByProvider("google", userInfo.id);
  if (!user) {
    user = await UserService.createUser({
      email: userInfo.email,
      name: userInfo.name,
      roles: ["user"],
      provider: "google",
      providerId: userInfo.id
    });
  }

  await UserService.updateLastLogin(user.id);

  const accessToken = await generateAccessToken(user);
  const refreshToken = await generateRefreshToken();
  await UserService.storeRefreshToken(refreshToken, user.id);

  const response: AuthResponse = {
    accessToken,
    refreshToken,
    user
  };

  ctx.response.body = response;
});

router.get("/github/callback", async (ctx) => {
  const code = ctx.request.url.searchParams.get("code");

  if (!code) {
    ctx.response.status = 400;
    ctx.response.body = { error: "Authorization code required" };
    return;
  }

  const userInfo = await OAuth2Provider.exchangeGithubCode(code);
  if (!userInfo) {
    ctx.response.status = 400;
    ctx.response.body = { error: "Failed to get user information" };
    return;
  }

  let user = await UserService.findByProvider("github", userInfo.id);
  if (!user) {
    user = await UserService.createUser({
      email: userInfo.email,
      name: userInfo.name,
      roles: ["user"],
      provider: "github",
      providerId: userInfo.id
    });
  }

  await UserService.updateLastLogin(user.id);

  const accessToken = await generateAccessToken(user);
  const refreshToken = await generateRefreshToken();
  await UserService.storeRefreshToken(refreshToken, user.id);

  const response: AuthResponse = {
    accessToken,
    refreshToken,
    user
  };

  ctx.response.body = response;
});

// Token refresh endpoint
router.post("/refresh", async (ctx) => {
  const body = await ctx.request.body({ type: "json" }).value;
  const refreshToken = body.refreshToken;

  if (!refreshToken) {
    ctx.response.status = 400;
    ctx.response.body = { error: "Refresh token required" };
    return;
  }

  const userId = await UserService.validateRefreshToken(refreshToken);
  if (!userId) {
    ctx.response.status = 401;
    ctx.response.body = { error: "Invalid or expired refresh token" };
    return;
  }

  const user = await UserService.findById(userId);
  if (!user) {
    ctx.response.status = 404;
    ctx.response.body = { error: "User not found" };
    return;
  }

  const newAccessToken = await generateAccessToken(user);
  const newRefreshToken = await generateRefreshToken();

  await UserService.revokeRefreshToken(refreshToken);
  await UserService.storeRefreshToken(newRefreshToken, user.id);

  ctx.response.body = {
    accessToken: newAccessToken,
    refreshToken: newRefreshToken
  };
});

// Logout endpoint
router.post("/logout", authenticateToken, async (ctx) => {
  const body = await ctx.request.body({ type: "json" }).value;
  const refreshToken = body.refreshToken;

  if (refreshToken) {
    await UserService.revokeRefreshToken(refreshToken);
  }

  ctx.response.body = { message: "Logged out successfully" };
});

// Get current user profile
router.get("/me", authenticateToken, async (ctx) => {
  const user = await UserService.findById(ctx.user!.sub);
  if (!user) {
    ctx.response.status = 404;
    ctx.response.body = { error: "User not found" };
    return;
  }

  ctx.response.body = { user };
});

export default router;

Create protected API routes

Implement example API endpoints with role-based access control to demonstrate how to secure different parts of your application.

import { Router } from "https://deno.land/x/oak@v15.0.0/mod.ts";
import { authenticateToken, requireRoles, optionalAuth } from "../middleware/auth.ts";

const router = new Router({ prefix: "/api" });

// Public endpoint (no authentication required)
router.get("/public", (ctx) => {
  ctx.response.body = {
    message: "This is a public endpoint",
    timestamp: new Date().toISOString()
  };
});

// Endpoint with optional authentication
router.get("/optional", optionalAuth, (ctx) => {
  const message = ctx.user 
    ? Welcome back, ${ctx.user.name}! 
    : "This endpoint works for both authenticated and anonymous users";
  
  ctx.response.body = {
    message,
    user: ctx.user || null,
    timestamp: new Date().toISOString()
  };
});

// Protected endpoint (authentication required)
router.get("/protected", authenticateToken, (ctx) => {
  ctx.response.body = {
    message: Hello ${ctx.user!.name}, you are authenticated!,
    user: {
      id: ctx.user!.sub,
      email: ctx.user!.email,
      roles: ctx.user!.roles
    },
    timestamp: new Date().toISOString()
  };
});

// Admin-only endpoint
router.get("/admin", authenticateToken, requireRoles("admin"), (ctx) => {
  ctx.response.body = {
    message: "This is an admin-only endpoint",
    user: ctx.user!.email,
    adminData: {
      systemStatus: "operational",
      activeUsers: 42,
      serverUptime: "5 days, 12 hours"
    }
  };
});

// Moderator or admin endpoint
router.get("/moderate", authenticateToken, requireRoles("moderator", "admin"), (ctx) => {
  ctx.response.body = {
    message: "This endpoint requires moderator or admin role",
    user: ctx.user!.email,
    moderationData: {
      pendingReports: 3,
      flaggedContent: 8
    }
  };
});

// User profile management (users can only access their own data)
router.get("/users/:id", authenticateToken, async (ctx) => {
  const requestedUserId = ctx.params.id;
  const currentUserId = ctx.user!.sub;
  
  // Users can only access their own profile unless they're admin
  if (requestedUserId !== currentUserId && !ctx.user!.roles.includes("admin")) {
    ctx.response.status = 403;
    ctx.response.body = { error: "Access denied: You can only access your own profile" };
    return;
  }

  const { UserService } = await import("../utils/users.ts");
  const user = await UserService.findById(requestedUserId);
  
  if (!user) {
    ctx.response.status = 404;
    ctx.response.body = { error: "User not found" };
    return;
  }

  ctx.response.body = {
    user: {
      id: user.id,
      email: user.email,
      name: user.name,
      roles: user.roles,
      provider: user.provider,
      createdAt: user.createdAt,
      lastLogin: user.lastLogin
    }
  };
});

export default router;

Create the main application server

Set up the main Deno server with Oak framework, CORS handling, error management, and route registration.

import { Application, Router } from "https://deno.land/x/oak@v15.0.0/mod.ts";
import { oakCors } from "https://deno.land/x/cors@v1.2.2/mod.ts";
import authRoutes from "./routes/auth.ts";
import apiRoutes from "./routes/api.ts";

const app = new Application();
const router = new Router();

// CORS configuration
app.use(oakCors({
  origin: ["http://localhost:3000", "http://localhost:8000"], // Add your frontend URLs
  methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
  allowedHeaders: ["Content-Type", "Authorization"],
  credentials: true
}));

// Error handling middleware
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (error) {
    console.error("Server error:", error);
    ctx.response.status = 500;
    ctx.response.body = {
      error: "Internal server error",
      message: Deno.env.get("NODE_ENV") === "development" ? error.message : "Something went wrong"
    };
  }
});

// Request logging middleware
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  console.log(${ctx.request.method} ${ctx.request.url.pathname} - ${ctx.response.status} - ${ms}ms);
});

// Health check endpoint
router.get("/health", (ctx) => {
  ctx.response.body = {
    status: "healthy",
    timestamp: new Date().toISOString(),
    uptime: Math.floor(performance.now() / 1000)
  };
});

// API documentation endpoint
router.get("/", (ctx) => {
  ctx.response.body = {
    name: "Deno JWT Authentication API",
    version: "1.0.0",
    endpoints: {
      auth: {
        "GET /auth/google": "Get Google OAuth2 authorization URL",
        "GET /auth/github": "Get GitHub OAuth2 authorization URL",
        "GET /auth/google/callback": "Google OAuth2 callback",
        "GET /auth/github/callback": "GitHub OAuth2 callback",
        "POST /auth/refresh": "Refresh access token",
        "POST /auth/logout": "Logout user",
        "GET /auth/me": "Get current user profile"
      },
      api: {
        "GET /api/public": "Public endpoint (no auth required)",
        "GET /api/optional": "Optional auth endpoint",
        "GET /api/protected": "Protected endpoint (auth required)",
        "GET /api/admin": "Admin-only endpoint",
        "GET /api/moderate": "Moderator/admin endpoint",
        "GET /api/users/:id": "Get user profile"
      },
      utility: {
        "GET /health": "Health check endpoint",
        "GET /": "API documentation"
      }
    }
  };
});

// Register routes
app.use(router.routes());
app.use(router.allowedMethods());
app.use(authRoutes.routes());
app.use(authRoutes.allowedMethods());
app.use(apiRoutes.routes());
app.use(apiRoutes.allowedMethods());

// Handle 404
app.use((ctx) => {
  ctx.response.status = 404;
  ctx.response.body = { error: "Not found" };
});

const port = parseInt(Deno.env.get("PORT") || "8000");
console.log(🚀 Server starting on http://localhost:${port});
console.log(📚 API documentation available at http://localhost:${port});

await app.listen({ port });

Create environment configuration

Set up environment variables for secure configuration management. Never commit secrets to version control.

# JWT Configuration
JWT_SECRET=your-super-secret-jwt-key-change-in-production-minimum-256-bits

Google OAuth2

GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com GOOGLE_CLIENT_SECRET=your-google-client-secret GOOGLE_REDIRECT_URI=http://localhost:8000/auth/google/callback

GitHub OAuth2

GITHUB_CLIENT_ID=your-github-client-id GITHUB_CLIENT_SECRET=your-github-client-secret GITHUB_REDIRECT_URI=http://localhost:8000/auth/github/callback

Server Configuration

PORT=8000 NODE_ENV=development
Security Warning: Always use strong, randomly generated JWT secrets in production. The secret should be at least 256 bits (32 characters) long. Store all sensitive configuration in environment variables, never in code.

Create startup script

Create a script to run your Deno application with the necessary permissions and environment loading.

#!/bin/bash

Load environment variables

export $(cat .env | grep -v '^#' | xargs)

Run Deno application with required permissions

deno run \ --allow-net \ --allow-env \ --allow-read \ --watch \ main.ts
chmod +x run.sh

Start the authentication server

Launch your Deno JWT authentication server and verify it's running correctly with proper permissions.

./run.sh

Verify your setup

Test your JWT authentication implementation by checking various endpoints and authentication flows.

# Check server health
curl http://localhost:8000/health

Test public endpoint

curl http://localhost:8000/api/public

Get Google OAuth2 authorization URL

curl http://localhost:8000/auth/google

Get GitHub OAuth2 authorization URL

curl http://localhost:8000/auth/github

Test protected endpoint (should return 401)

curl -H "Authorization: Bearer invalid-token" http://localhost:8000/api/protected

For complete OAuth2 testing, you'll need to configure your OAuth2 applications in the Google Console and GitHub Developer Settings, then use a web browser to complete the authorization flow.

Common issues

SymptomCauseFix
"JWT verification failed" errorsWrong JWT secret or malformed tokenVerify JWT_SECRET matches between token generation and verification
OAuth2 callback returns 400 errorInvalid client ID/secret or wrong redirect URICheck OAuth2 provider configuration and redirect URI matching
"Token expired" on fresh tokensServer time synchronization issuesEnsure server time is synchronized with NTP
CORS errors from frontendMissing or incorrect CORS configurationAdd your frontend domain to the CORS origin list
"Access token required" on authenticated requestsMissing or malformed Authorization headerInclude "Authorization: Bearer TOKEN" header
"Insufficient permissions" errorsUser lacks required rolesVerify user roles and endpoint requirements

Production considerations

Replace in-memory storage

The example uses in-memory user storage. For production, integrate with a proper database like PostgreSQL or MongoDB.

# Example database integration
deno add @deno/postgres
deno add mongodb

Implement rate limiting

Add rate limiting to prevent brute force attacks and API abuse, especially on authentication endpoints.

import { RateLimiter } from "https://deno.land/x/oak_rate_limit@v1.0.0/mod.ts";

export const authRateLimit = RateLimiter({
  windowMs: 15  60  1000, // 15 minutes
  max: 5, // limit each IP to 5 requests per windowMs
  message: "Too many authentication attempts, please try again later"
});

Add request validation

Implement input validation using a schema validation library to ensure request data integrity and security.

deno add @valibot/valibot

You can extend this foundation by integrating with existing database tutorials like Configure Deno database connections to PostgreSQL and Redis with connection pooling for persistent storage, or enhance security by implementing additional measures from Set up Node.js application security with Helmet and rate limiting.

Next steps

Automated install script

Run this to automate the entire setup

#deno #jwt #oauth2 #authentication #api-security

Need help?

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.

Talk to an engineer