Last active
March 10, 2026 11:43
-
-
Save Gaurav8757/f9026d4662d4de0dfb27ca6f847c5d4a to your computer and use it in GitHub Desktop.
AccessToken vs RefreshToken
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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