用户变动撤销所有刷新token,立即失效accesstoken
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { invalidateDeviceAccessToken } from '@/lib/access-token-invalidation';
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getConfig } from '@/lib/config';
|
||||
import { db, getStorage } from '@/lib/db';
|
||||
@@ -156,6 +157,7 @@ export async function DELETE(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
invalidateDeviceAccessToken(targetUsername, tokenId);
|
||||
await revokeRefreshToken(targetUsername, tokenId);
|
||||
const storage = getStorage();
|
||||
await storage.deletePushSubscriptionsByTokenId?.(targetUsername, tokenId);
|
||||
|
||||
@@ -2,14 +2,29 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { invalidateUserAccessTokens } from '@/lib/access-token-invalidation';
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getConfig } from '@/lib/config';
|
||||
import { db } from '@/lib/db';
|
||||
import { db, getStorage } from '@/lib/db';
|
||||
import { sanitizeFeaturePermissions } from '@/lib/feature-permissions';
|
||||
import { revokeAllRefreshTokens } from '@/lib/refresh-token';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
// 支持的操作类型
|
||||
|
||||
async function terminateUserSessions(username: string, reason: string): Promise<void> {
|
||||
try {
|
||||
invalidateUserAccessTokens(username);
|
||||
await revokeAllRefreshTokens(username);
|
||||
const storage = getStorage();
|
||||
await storage.deleteAllPushSubscriptions?.(username);
|
||||
console.log(`Terminated all sessions for ${username}: ${reason}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to terminate sessions for ${username}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
const ACTIONS = [
|
||||
'add',
|
||||
'ban',
|
||||
@@ -194,6 +209,7 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
// 只更新V2存储
|
||||
await db.updateUserInfoV2(targetUsername!, { banned: true });
|
||||
await terminateUserSessions(targetUsername!, 'user banned');
|
||||
break;
|
||||
}
|
||||
case 'unban': {
|
||||
@@ -262,6 +278,7 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
// 只更新V2存储
|
||||
await db.updateUserInfoV2(targetUsername!, { role: 'user' });
|
||||
await terminateUserSessions(targetUsername!, 'admin role revoked');
|
||||
break;
|
||||
}
|
||||
case 'changePassword': {
|
||||
@@ -296,6 +313,7 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
// 使用新版本修改密码(SHA256加密)
|
||||
await db.changePasswordV2(targetUsername!, targetPassword);
|
||||
await terminateUserSessions(targetUsername!, 'password changed by admin');
|
||||
break;
|
||||
}
|
||||
case 'deleteUser': {
|
||||
@@ -321,7 +339,8 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// 只删除V2存储中的用户
|
||||
// 先终止目标用户所有会话,再删除V2存储中的用户
|
||||
await terminateUserSessions(targetUsername!, 'user deleted');
|
||||
await db.deleteUserV2(targetUsername!);
|
||||
|
||||
break;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { invalidateDeviceAccessToken, invalidateUserAccessTokens } from '@/lib/access-token-invalidation';
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getStorage } from '@/lib/db';
|
||||
import {
|
||||
@@ -51,6 +52,7 @@ export async function DELETE(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'Token ID required' }, { status: 400 });
|
||||
}
|
||||
|
||||
invalidateDeviceAccessToken(authInfo.username, tokenId);
|
||||
await revokeRefreshToken(authInfo.username, tokenId);
|
||||
const storage = getStorage();
|
||||
await storage.deletePushSubscriptionsByTokenId?.(authInfo.username, tokenId);
|
||||
@@ -71,6 +73,7 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
try {
|
||||
invalidateUserAccessTokens(authInfo.username);
|
||||
await revokeAllRefreshTokens(authInfo.username);
|
||||
const storage = getStorage();
|
||||
await storage.deleteAllPushSubscriptions?.(authInfo.username);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie, parseAuthInfo } from '@/lib/auth';
|
||||
import { db } from '@/lib/db';
|
||||
import { refreshAccessToken } from '@/lib/middleware-auth';
|
||||
import { TOKEN_CONFIG } from '@/lib/refresh-token';
|
||||
|
||||
@@ -73,6 +74,26 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
if (authInfo.username === process.env.USERNAME) {
|
||||
if (authInfo.role !== 'owner') {
|
||||
return NextResponse.json({ error: 'Role changed' }, { status: 401 });
|
||||
}
|
||||
} else {
|
||||
const userInfo = await db.getUserInfoV2(authInfo.username);
|
||||
|
||||
if (!userInfo) {
|
||||
return NextResponse.json({ error: 'User not found' }, { status: 401 });
|
||||
}
|
||||
|
||||
if (userInfo.banned) {
|
||||
return NextResponse.json({ error: 'User banned' }, { status: 403 });
|
||||
}
|
||||
|
||||
if (userInfo.role !== authInfo.role) {
|
||||
return NextResponse.json({ error: 'Role changed' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
|
||||
// 只检查 Refresh Token 是否过期
|
||||
if (now >= authInfo.refreshExpires) {
|
||||
return NextResponse.json(
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { invalidateDeviceAccessToken } from '@/lib/access-token-invalidation';
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getStorage } from '@/lib/db';
|
||||
import { db } from '@/lib/db';
|
||||
@@ -59,6 +60,7 @@ export async function POST(request: NextRequest) {
|
||||
// 撤销所有非当前设备的 token
|
||||
for (const device of devices) {
|
||||
if (device.tokenId !== currentTokenId) {
|
||||
invalidateDeviceAccessToken(username, device.tokenId);
|
||||
await revokeRefreshToken(username, device.tokenId);
|
||||
await storage.deletePushSubscriptionsByTokenId?.(username, device.tokenId);
|
||||
console.log(`Revoked token ${device.tokenId} for ${username} after password change`);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { invalidateDeviceAccessToken } from '@/lib/access-token-invalidation';
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getStorage } from '@/lib/db';
|
||||
import { revokeRefreshToken } from '@/lib/refresh-token';
|
||||
@@ -12,6 +13,7 @@ export async function POST(request: NextRequest) {
|
||||
// 撤销当前设备的 Refresh Token
|
||||
if (authInfo && authInfo.username && authInfo.tokenId) {
|
||||
try {
|
||||
invalidateDeviceAccessToken(authInfo.username, authInfo.tokenId);
|
||||
await revokeRefreshToken(authInfo.username, authInfo.tokenId);
|
||||
const storage = getStorage();
|
||||
await storage.deletePushSubscriptionsByTokenId?.(authInfo.username, authInfo.tokenId);
|
||||
|
||||
Reference in New Issue
Block a user