Skip to content

Instantly share code, notes, and snippets.

@Gaurav8757
Last active March 10, 2026 11:43
Show Gist options
  • Select an option

  • Save Gaurav8757/f9026d4662d4de0dfb27ca6f847c5d4a to your computer and use it in GitHub Desktop.

Select an option

Save Gaurav8757/f9026d4662d4de0dfb27ca6f847c5d4a to your computer and use it in GitHub Desktop.
AccessToken vs RefreshToken
// services
// Verify OTP & issue tokens
export async function verifyOtp(mobile) {
// Find or create user
let user = await prisma.user.findUnique({ where: { mobile } });
// ✅ If not found, create user
// if (!user) {
// user = await prisma.user.create({
// data: {
// mobile,
// role: "USER",
// },
// });
// }
// ✅ Prepare payload for JWT
const payload = {
id: user.id,
mobile: user.mobile,
role: user.role,
isVerified: true, // before hard set true for token payload isverified is should be true
};
// ✅ Generate new tokens
const accessToken = generateAccessToken(payload);
const refreshToken = generateRefreshToken(payload);
// ✅ Update user with tokens + verified status
const updatedUser = await prisma.user?.update({
where: { id: user?.id },
data: {
accessToken,
refreshToken,
isVerified: true,
},
});
// ✅ Return updated user and tokens
return { user: updatedUser, accessToken, refreshToken };
}
// verify nextapi
import { NextResponse } from "next/server";
import redis from "@/app/api-services/redisClient";
import { protectRequest } from "@/app/api-services/arcjetService";
import { apiError } from "@/lib/api-errors";
import { verifyOtp } from "@/app/api-services/authService";
export async function POST(req) {
try {
const { mobile, otp } = await req.json();
if (!mobile) {
return new NextResponse(
JSON.stringify({ error: "Mobile Number is required." }),
{ status: 400 }
);
}
if (!otp) {
return new NextResponse(JSON.stringify({ error: "OTP is required." }), {
status: 400,
});
}
// Optional: bot protection
const decision = await protectRequest(req, {
userId: mobile,
requested: 1,
});
if (decision.isDenied()) {
return new NextResponse(
JSON.stringify({
error: "Blocked by Our Internal Server",
reason: decision.reason,
}),
{ status: 429 }
);
}
const cachedOtp = await redis.get(`otp:${mobile}`);
if (!cachedOtp || cachedOtp !== otp.trim()) {
return new NextResponse(
JSON.stringify({ error: "Invalid or expired OTP" }),
{ status: 401 }
);
}
// OTP valid → remove OTP
await redis.del(`otp:${mobile}`);
// Generate JWT tokens inside service
const { user, accessToken, refreshToken } = await verifyOtp(mobile);
// Store refresh token in Redis (for revocation & verification)
await redis.set(`refresh_token:${mobile}`, refreshToken, {
EX: 7 * 24 * 60 * 60,
}); // 7 days
// Don't Use it not secure
// return new NextResponse(
// JSON.stringify({
// message: "Login successful",
// isUserVerified: user.isVerified,
// accessToken,
// refreshToken,
// }),
// { status: 200 }
// );
// Use cookies
→ This prevents XSS (since cookies are not accessible via document.cookie).
→ Tokens are automatically sent with each API call to your domain.
⚡ Option B — For Mobile Apps
Store in Secure Storage (like Keychain / Keystore) — never in AsyncStorage or localStorage.
// Example using NextResponse cookies
const response = NextResponse.json({ message: "Login successful" });
response.cookies.set("access_token", accessToken, {
httpOnly: true,
secure: true,
sameSite: "Strict",
maxAge: 15 * 60, // 15 min gives GMT timezone
});
response.cookies.set("refresh_token", refreshToken, {
httpOnly: true,
secure: true,
sameSite: "Strict",
maxAge: 7 * 24 * 60 * 60, // 7 days gives GMT timezone
});
return response;
} catch (err) {
return apiError(err);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment