fix: 修复oauth-endpoint相关问题,并允许重复注册处理器

This commit is contained in:
Pan Qiancheng 2025-12-24 15:13:16 +08:00
parent ba434da707
commit a3ec5fc808
7 changed files with 1367 additions and 1250 deletions

View File

@ -26,271 +26,284 @@ const oauthTokenEndpoint = {
type: "free",
fn: async (contextBuilder, params, header, req, body) => {
const context = await contextBuilder();
const { client_id, client_secret, grant_type, code, redirect_uri, code_verifier // PKCE支持
} = body;
const [app] = await context.select("oauthApplication", {
data: {
id: 1,
clientSecret: 1,
},
filter: {
id: client_id,
}
}, {});
if (!app) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_client", error_description: "Client not found", success: false }
};
}
if (app.clientSecret !== client_secret) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_client", error_description: "Client secret mismatch", success: false }
};
}
// grant_type几种类型 目前只支持authorization_code
if (grant_type !== "authorization_code") {
await context.commit();
return {
statusCode: 400,
data: { error: "unsupported_grant_type", error_description: "Only authorization_code grant type is supported", success: false }
};
}
// 找code的记录
const [authCodeRecord] = await context.select("oauthAuthorizationCode", {
data: {
id: 1,
code: 1,
redirectUri: 1,
userId: 1,
expiresAt: 1,
usedAt: 1,
codeChallenge: 1,
codeChallengeMethod: 1,
},
filter: {
code,
}
}, {});
// 找不到记录
if (!authCodeRecord) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Invalid authorization code", success: false }
};
}
// PKCE 验证
if (authCodeRecord.codeChallenge) {
if (!code_verifier) {
await context.commit();
return {
statusCode: 400,
data: {
error: "invalid_request",
error_description: "code_verifier is required",
success: false
}
};
}
// 验证 code_verifier
const isValid = await verifyPKCE(code_verifier, authCodeRecord.codeChallenge, authCodeRecord.codeChallengeMethod);
if (!isValid) {
await context.commit();
return {
statusCode: 400,
data: {
error: "invalid_grant",
error_description: "Invalid code_verifier",
success: false
}
};
}
}
// 验证redirect_uri
if (authCodeRecord.redirectUri !== redirect_uri) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Redirect URI mismatch", success: false }
};
}
// 验证过期
if (authCodeRecord.expiresAt < Date.now()) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Authorization code expired", success: false }
};
}
// 验证是否已使用
if (authCodeRecord.usedAt) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Authorization code already used", success: false }
};
}
// 如果userIdcode里面的appId都一样则说明是这个用户在这个应用下第二次授权的直接返回
const [existingToken] = await context.select("oauthToken", {
data: {
id: 1,
accessToken: 1,
refreshToken: 1,
accessExpiresAt: 1,
refreshExpiresAt: 1,
},
filter: {
userId: authCodeRecord.userId,
code: {
oauthAppId: app.id,
},
revokedAt: {
$exists: false,
}
}
}, {});
// 如果存在且未过期,直接返回
if (existingToken && (existingToken.accessExpiresAt > Date.now())) {
console.log("Existing valid token found, returning it directly");
// 刷新最后一次使用
await context.operate("oauthToken", {
id: await generateNewIdAsync(),
action: "update",
try {
const { client_id, client_secret, grant_type, code, redirect_uri, code_verifier // PKCE支持
} = body;
const [app] = await context.select("oauthApplication", {
data: {
lastUsedAt: Date.now(),
id: 1,
clientSecret: 1,
},
filter: {
id: existingToken.id,
id: client_id,
}
}, {});
// // 创建记录
// await context.operate("oauthUserAuthorization", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// tokenId: existingToken.id,
// usageState: 'granted',
// },
// filter: {
// codeId: authCodeRecord.id,
// }
// }, {})
await context.commit();
return {
statusCode: 200,
if (!app) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_client", error_description: "Client not found", success: false }
};
}
if (app.clientSecret !== client_secret) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_client", error_description: "Client secret mismatch", success: false }
};
}
// grant_type几种类型 目前只支持authorization_code
if (grant_type !== "authorization_code") {
await context.commit();
return {
statusCode: 400,
data: { error: "unsupported_grant_type", error_description: "Only authorization_code grant type is supported", success: false }
};
}
// 找code的记录
const [authCodeRecord] = await context.select("oauthAuthorizationCode", {
data: {
access_token: existingToken.accessToken,
token_type: "Bearer",
expires_in: existingToken.accessExpiresAt - Date.now(),
refresh_token: existingToken.refreshToken,
refresh_expires_in: existingToken.refreshExpiresAt - Date.now(),
success: true,
id: 1,
code: 1,
redirectUri: 1,
userId: 1,
expiresAt: 1,
usedAt: 1,
codeChallenge: 1,
codeChallengeMethod: 1,
},
filter: {
code,
}
};
}
const expiresIn = 3600; // 1 hour
const refreshTokenExpiresIn = 86400 * 30; // 30 days
// 如果过期就顺带刷新了(refresh token没过期 accessToken过期了)
if (existingToken && (existingToken.refreshExpiresAt > Date.now())) {
console.log("Existing token expired, refreshing it");
const newAccessToken = randomUUID();
const newRefreshToken = randomUUID();
}, {});
// 找不到记录
if (!authCodeRecord) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Invalid authorization code", success: false }
};
}
// PKCE 验证
if (authCodeRecord.codeChallenge) {
if (!code_verifier) {
await context.commit();
return {
statusCode: 400,
data: {
error: "invalid_request",
error_description: "code_verifier is required",
success: false
}
};
}
// 验证 code_verifier
const isValid = await verifyPKCE(code_verifier, authCodeRecord.codeChallenge, authCodeRecord.codeChallengeMethod);
if (!isValid) {
await context.commit();
return {
statusCode: 400,
data: {
error: "invalid_grant",
error_description: "Invalid code_verifier",
success: false
}
};
}
}
// 验证redirect_uri
if (authCodeRecord.redirectUri !== redirect_uri) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Redirect URI mismatch", success: false }
};
}
// 验证过期
if (authCodeRecord.expiresAt < Date.now()) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Authorization code expired", success: false }
};
}
// 验证是否已使用
if (authCodeRecord.usedAt) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Authorization code already used", success: false }
};
}
// 如果userIdcode里面的appId都一样则说明是这个用户在这个应用下第二次授权的直接返回
const [existingToken] = await context.select("oauthToken", {
data: {
id: 1,
accessToken: 1,
refreshToken: 1,
accessExpiresAt: 1,
refreshExpiresAt: 1,
},
filter: {
userId: authCodeRecord.userId,
code: {
oauthAppId: app.id,
},
revokedAt: {
$exists: false,
}
}
}, {});
// 如果存在且未过期,直接返回
if (existingToken && (existingToken.accessExpiresAt > Date.now())) {
console.log("Existing valid token found, returning it directly");
// 刷新最后一次使用
await context.operate("oauthToken", {
id: await generateNewIdAsync(),
action: "update",
data: {
lastUsedAt: Date.now(),
},
filter: {
id: existingToken.id,
}
}, {});
// // 创建记录
// await context.operate("oauthUserAuthorization", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// tokenId: existingToken.id,
// usageState: 'granted',
// },
// filter: {
// codeId: authCodeRecord.id,
// }
// }, {})
await context.commit();
return {
statusCode: 200,
data: {
access_token: existingToken.accessToken,
token_type: "Bearer",
expires_in: existingToken.accessExpiresAt - Date.now(),
refresh_token: existingToken.refreshToken,
refresh_expires_in: existingToken.refreshExpiresAt - Date.now(),
success: true,
}
};
}
const expiresIn = 3600; // 1 hour
const refreshTokenExpiresIn = 86400 * 30; // 30 days
// 如果过期就顺带刷新了(refresh token没过期 accessToken过期了)
if (existingToken && (existingToken.refreshExpiresAt > Date.now())) {
console.log("Existing token expired, refreshing it");
const newAccessToken = randomUUID();
const newRefreshToken = randomUUID();
await context.operate("oauthToken", {
id: await generateNewIdAsync(),
action: "update",
data: {
lastUsedAt: Date.now(),
accessToken: newAccessToken,
refreshToken: newRefreshToken,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
},
filter: {
id: existingToken.id,
}
}, {});
// // 创建记录
// await context.operate("oauthUserAuthorization", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// tokenId: existingToken.id,
// usageState: 'granted',
// },
// filter: {
// codeId: authCodeRecord.id,
// }
// }, {})
await context.commit();
return {
statusCode: 200,
data: {
access_token: newAccessToken,
token_type: "Bearer",
expires_in: expiresIn,
refresh_token: newRefreshToken,
refresh_expires_in: refreshTokenExpiresIn,
success: true,
}
};
}
// 有一种情况是access和refresh都过期了但是用户又重新用code来换token
// 这种情况下不能刷新老的,也要当作新的处理
// 创建accessToken
const genaccessToken = randomUUID();
const refreshToken = randomUUID();
console.log("Creating new access token and refresh token");
// create
const tokenId = await generateNewIdAsync();
await context.operate("oauthToken", {
id: await generateNewIdAsync(),
action: "create",
data: {
id: tokenId,
accessToken: genaccessToken,
refreshToken: refreshToken,
userId: authCodeRecord.userId,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
codeId: authCodeRecord.id,
}
}, {});
// 创建记录
await context.operate("oauthUserAuthorization", {
id: await generateNewIdAsync(),
action: "update",
data: {
lastUsedAt: Date.now(),
accessToken: newAccessToken,
refreshToken: newRefreshToken,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
tokenId: tokenId,
usageState: 'granted',
},
filter: {
codeId: authCodeRecord.id,
}
}, {});
// 标记code为已使用
await context.operate("oauthAuthorizationCode", {
id: await generateNewIdAsync(),
action: "update",
data: {
usedAt: Date.now(),
},
filter: {
id: authCodeRecord.id,
}
}, {});
// // 创建记录
// await context.operate("oauthUserAuthorization", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// tokenId: existingToken.id,
// usageState: 'granted',
// },
// filter: {
// codeId: authCodeRecord.id,
// }
// }, {})
await context.commit();
return {
statusCode: 200,
data: {
access_token: newAccessToken,
access_token: genaccessToken,
token_type: "Bearer",
expires_in: expiresIn,
refresh_token: newRefreshToken,
refresh_token: refreshToken,
refresh_expires_in: refreshTokenExpiresIn,
success: true,
}
};
}
// 有一种情况是access和refresh都过期了但是用户又重新用code来换token
// 这种情况下不能刷新老的,也要当作新的处理
// 创建accessToken
const genaccessToken = randomUUID();
const refreshToken = randomUUID();
console.log("Creating new access token and refresh token");
// create
const tokenId = await generateNewIdAsync();
await context.operate("oauthToken", {
id: await generateNewIdAsync(),
action: "create",
data: {
id: tokenId,
accessToken: genaccessToken,
refreshToken: refreshToken,
userId: authCodeRecord.userId,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
codeId: authCodeRecord.id,
}
}, {});
// 创建记录
await context.operate("oauthUserAuthorization", {
id: await generateNewIdAsync(),
action: "update",
data: {
tokenId: tokenId,
usageState: 'granted',
},
filter: {
codeId: authCodeRecord.id,
}
}, {});
// 标记code为已使用
await context.operate("oauthAuthorizationCode", {
id: await generateNewIdAsync(),
action: "update",
data: {
usedAt: Date.now(),
},
filter: {
id: authCodeRecord.id,
}
}, {});
await context.commit();
return {
statusCode: 200,
data: {
access_token: genaccessToken,
token_type: "Bearer",
expires_in: expiresIn,
refresh_token: refreshToken,
refresh_expires_in: refreshTokenExpiresIn,
success: true,
}
};
catch (err) {
console.error("Error in oauth token endpoint:", err);
await context.rollback();
return {
statusCode: 500,
data: { error: "server_error", error_description: "Internal server error: " + (err instanceof Error ? err.message : String(err)), success: false }
};
}
}
};
const oauthUserInfoEndpoint = {
@ -301,37 +314,47 @@ const oauthUserInfoEndpoint = {
fn: async (contextBuilder, params, header, req, body) => {
const context = await contextBuilder();
const token = header.authorization; // Bearer token
const checkResult = await checkOauthTokenAvaliable(context, token);
if (checkResult.error) {
try {
const checkResult = await checkOauthTokenAvaliable(context, token);
if (checkResult.error) {
await context.commit();
return {
statusCode: checkResult.statusCode || 401,
data: { error: checkResult.error, success: false }
};
}
const tokenRecord = checkResult.tokenRecord;
assert(tokenRecord?.user, "User must be present in token record");
assert(tokenRecord?.code?.application, "Application must be present in token record");
const extrafile = tokenRecord.user.extraFile$entity?.[0];
const application = tokenRecord.code.application;
let avatarUrl = '';
if (extrafile) {
avatarUrl = await composeFileUrlBackend(application, extrafile, context);
}
await context.commit();
return {
statusCode: checkResult.statusCode || 401,
data: { error: checkResult.error, success: false }
statusCode: 200, data: {
userInfo: {
id: tokenRecord.user.id,
name: tokenRecord.user.name,
nickname: tokenRecord.user.nickname,
birth: tokenRecord.user.birth,
gender: tokenRecord.user.gender,
avatarUrl: avatarUrl,
},
error: null
}
};
}
const tokenRecord = checkResult.tokenRecord;
assert(tokenRecord?.user, "User must be present in token record");
assert(tokenRecord?.code?.application, "Application must be present in token record");
const extrafile = tokenRecord.user.extraFile$entity?.[0];
const application = tokenRecord.code.application;
let avatarUrl = '';
if (extrafile) {
avatarUrl = await composeFileUrlBackend(application, extrafile, context);
catch (err) {
console.error("Error in oauth userinfo endpoint:", err);
await context.rollback();
return {
statusCode: 500,
data: { error: "server_error", error_description: "Internal server error: " + (err instanceof Error ? err.message : String(err)), success: false }
};
}
await context.commit();
return {
statusCode: 200, data: {
userInfo: {
id: tokenRecord.user.id,
name: tokenRecord.user.name,
nickname: tokenRecord.user.nickname,
birth: tokenRecord.user.birth,
gender: tokenRecord.user.gender,
avatarUrl: avatarUrl,
},
error: null
}
};
}
};
const refreshTokenEndpoint = {
@ -366,88 +389,98 @@ const refreshTokenEndpoint = {
};
}
const context = await contextBuilder();
const [oauthApp] = await context.select("oauthApplication", {
data: {
id: 1,
},
filter: {
clientSecret: client_secret,
id: client_id,
try {
const [oauthApp] = await context.select("oauthApplication", {
data: {
id: 1,
},
filter: {
clientSecret: client_secret,
id: client_id,
}
}, {});
if (!oauthApp) {
await context.commit();
return {
statusCode: 401,
data: { error: "invalid_client", error_description: "Client authentication failed", success: false }
};
}
}, {});
if (!oauthApp) {
await context.commit();
return {
statusCode: 401,
data: { error: "invalid_client", error_description: "Client authentication failed", success: false }
};
}
const [tokenRecord] = await context.select("oauthToken", {
data: {
id: 1,
userId: 1,
accessToken: 1,
accessExpiresAt: 1,
refreshToken: 1,
refreshExpiresAt: 1,
code: {
applicationId: 1,
oauthApp: {
id: 1,
const [tokenRecord] = await context.select("oauthToken", {
data: {
id: 1,
userId: 1,
accessToken: 1,
accessExpiresAt: 1,
refreshToken: 1,
refreshExpiresAt: 1,
code: {
applicationId: 1,
oauthApp: {
id: 1,
}
}
},
filter: {
refreshToken: refresh_token,
code: {
oauthAppId: oauthApp.id,
}
}
},
filter: {
refreshToken: refresh_token,
code: {
oauthAppId: oauthApp.id,
}, {});
if (!tokenRecord) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Invalid refresh token", success: false }
};
}
if (tokenRecord.refreshExpiresAt < Date.now()) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Refresh token expired", success: false }
};
}
// 生成新的令牌
const newAccessToken = randomUUID();
const newRefreshToken = randomUUID();
const expiresIn = 3600; // 1 hour
const refreshTokenExpiresIn = 86400 * 30; // 30 days
await context.operate("oauthToken", {
id: await generateNewIdAsync(),
action: "update",
data: {
accessToken: newAccessToken,
refreshToken: newRefreshToken,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
},
filter: {
id: tokenRecord.id,
}
}
}, {});
if (!tokenRecord) {
}, {});
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Invalid refresh token", success: false }
statusCode: 200,
data: {
access_token: newAccessToken,
token_type: "Bearer",
expires_in: expiresIn,
refresh_token: newRefreshToken,
refresh_expires_in: refreshTokenExpiresIn,
success: true,
}
};
}
if (tokenRecord.refreshExpiresAt < Date.now()) {
await context.commit();
catch (err) {
console.error("Error in refresh token endpoint:", err);
await context.rollback();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Refresh token expired", success: false }
statusCode: 500,
data: { error: "server_error", error_description: "Internal server error: " + (err instanceof Error ? err.message : String(err)), success: false }
};
}
// 生成新的令牌
const newAccessToken = randomUUID();
const newRefreshToken = randomUUID();
const expiresIn = 3600; // 1 hour
const refreshTokenExpiresIn = 86400 * 30; // 30 days
await context.operate("oauthToken", {
id: await generateNewIdAsync(),
action: "update",
data: {
accessToken: newAccessToken,
refreshToken: newRefreshToken,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
},
filter: {
id: tokenRecord.id,
}
}, {});
await context.commit();
return {
statusCode: 200,
data: {
access_token: newAccessToken,
token_type: "Bearer",
expires_in: expiresIn,
refresh_token: newRefreshToken,
refresh_expires_in: refreshTokenExpiresIn,
success: true,
}
};
}
};
const oauthRevocationEndpoint = {
@ -475,64 +508,74 @@ const oauthRevocationEndpoint = {
};
}
const context = await contextBuilder();
const [oauthApp] = await context.select("oauthApplication", {
data: { id: 1 },
filter: { clientSecret: client_secret, id: client_id }
}, {});
if (!oauthApp) {
try {
const [oauthApp] = await context.select("oauthApplication", {
data: { id: 1 },
filter: { clientSecret: client_secret, id: client_id }
}, {});
if (!oauthApp) {
await context.commit();
return {
statusCode: 401,
data: { error: "invalid_client", error_description: "Client authentication failed", success: false }
};
}
// 3. 查找令牌记录
let tokenRecord = null;
const tokenProjection = {
data: { id: 1, code: { oauthAppId: 1 } },
filter: {}
};
// 尝试查找 Refresh Token
if (!token_type_hint || token_type_hint === 'refresh_token') {
tokenProjection.filter = { refreshToken: token, code: { oauthAppId: oauthApp.id } };
[tokenRecord] = await context.select("oauthToken", tokenProjection, {});
}
// 如果没找到,且 hint 不是 'refresh_token',则尝试查找 Access Token
if (!tokenRecord && (!token_type_hint || token_type_hint === 'access_token')) {
tokenProjection.filter = { accessToken: token, code: { oauthAppId: oauthApp.id } };
[tokenRecord] = await context.select("oauthToken", tokenProjection, {});
}
// 4. 撤销操作(无论找到与否,都返回 200但如果找到则执行失效操作
if (tokenRecord) {
// const pastTime = Date.now() - 1000;
// // 将 Access Token 和 Refresh Token 的过期时间都设为过去,使其立即失效
// await context.operate("oauthToken", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// accessExpiresAt: pastTime,
// refreshExpiresAt: pastTime,
// },
// filter: {
// id: tokenRecord.id,
// }
// }, {});
// 使用这个token的认证记录都撤销掉在trigger里会自动设置 revokedAt
await context.operate("oauthUserAuthorization", {
id: await generateNewIdAsync(),
action: "revoke",
data: {},
filter: {
tokenId: tokenRecord.id,
}
}, {});
}
await context.commit();
// 5. RFC 7009 规定:令牌撤销成功或令牌无效时,返回 HTTP 200
return {
statusCode: 401,
data: { error: "invalid_client", error_description: "Client authentication failed", success: false }
statusCode: 200,
data: {}
};
}
// 3. 查找令牌记录
let tokenRecord = null;
const tokenProjection = {
data: { id: 1, code: { oauthAppId: 1 } },
filter: {}
};
// 尝试查找 Refresh Token
if (!token_type_hint || token_type_hint === 'refresh_token') {
tokenProjection.filter = { refreshToken: token, code: { oauthAppId: oauthApp.id } };
[tokenRecord] = await context.select("oauthToken", tokenProjection, {});
catch (err) {
console.error("Error in oauth token revocation endpoint:", err);
await context.rollback();
return {
statusCode: 500,
data: { error: "server_error", error_description: "Internal server error: " + (err instanceof Error ? err.message : String(err)), success: false }
};
}
// 如果没找到,且 hint 不是 'refresh_token',则尝试查找 Access Token
if (!tokenRecord && (!token_type_hint || token_type_hint === 'access_token')) {
tokenProjection.filter = { accessToken: token, code: { oauthAppId: oauthApp.id } };
[tokenRecord] = await context.select("oauthToken", tokenProjection, {});
}
// 4. 撤销操作(无论找到与否,都返回 200但如果找到则执行失效操作
if (tokenRecord) {
// const pastTime = Date.now() - 1000;
// // 将 Access Token 和 Refresh Token 的过期时间都设为过去,使其立即失效
// await context.operate("oauthToken", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// accessExpiresAt: pastTime,
// refreshExpiresAt: pastTime,
// },
// filter: {
// id: tokenRecord.id,
// }
// }, {});
// 使用这个token的认证记录都撤销掉在trigger里会自动设置 revokedAt
await context.operate("oauthUserAuthorization", {
id: await generateNewIdAsync(),
action: "revoke",
data: {},
filter: {
tokenId: tokenRecord.id,
}
}, {});
}
await context.commit();
// 5. RFC 7009 规定:令牌撤销成功或令牌无效时,返回 HTTP 200
return {
statusCode: 200,
data: {}
};
}
};
const endpoints = {

View File

@ -3,9 +3,6 @@ import { applicationProjection, extraFileProjection } from "../../types/Projecti
import { getDefaultHandlers } from "./handler";
const handlerMap = new Map();
export const registerOauthUserinfoHandler = (type, handler) => {
if (handlerMap.has(type)) {
throw new Error(`oauth provider type ${type} 的 userinfo 处理器已注册`);
}
handlerMap.set(type, handler);
};
export const processUserInfo = (type, data) => {

View File

@ -127,7 +127,7 @@ async function loginByOauth(params, context) {
const { user } = existingOAuthUser;
const targetUser = user?.userState === 'merged' ? user.ref : user;
const tokenValue = await (0, token_1.setUpTokenAndUser)(env, context, 'oauthUser', existingOAuthUser.id, // 使用已存在的 oauthUser ID
undefined, targetUser // 关联的用户
undefined, targetUser // 关联的用户
);
// 更新登录信息
await context.operate("oauthUser", {
@ -172,7 +172,7 @@ async function loginByOauth(params, context) {
};
// 不传 user 参数,会自动创建新用户
const tokenValue = await (0, token_1.setUpTokenAndUser)(env, context, 'oauthUser', undefined, oauthUserCreateData, // 创建新的 oauthUser
undefined // 不传 user自动创建新用户
undefined // 不传 user自动创建新用户
);
await context.operate("oauthUser", {
id: await (0, uuid_1.generateNewIdAsync)(),
@ -367,19 +367,21 @@ async function authorize(params, context) {
throw new Error('unknown action');
}
const fetchOAuthUserInfo = async (code, providerConfig) => {
const params = {
grant_type: 'authorization_code',
code: code,
client_id: providerConfig.clientId,
client_secret: providerConfig.clientSecret,
redirect_uri: providerConfig.redirectUri,
}
console.log("使用 OAuth Code 获取 Access Token:", providerConfig.tokenEndpoint, params);
// 1. 使用 code 换取 access_token
const tokenResponse = await fetch(providerConfig.tokenEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
client_id: providerConfig.clientId,
client_secret: providerConfig.clientSecret,
redirect_uri: providerConfig.redirectUri,
}),
body: new URLSearchParams(params),
});
if (!tokenResponse.ok) {
const errorjson = await tokenResponse.json();

View File

@ -29,271 +29,284 @@ const oauthTokenEndpoint = {
type: "free",
fn: async (contextBuilder, params, header, req, body) => {
const context = await contextBuilder();
const { client_id, client_secret, grant_type, code, redirect_uri, code_verifier // PKCE支持
} = body;
const [app] = await context.select("oauthApplication", {
data: {
id: 1,
clientSecret: 1,
},
filter: {
id: client_id,
}
}, {});
if (!app) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_client", error_description: "Client not found", success: false }
};
}
if (app.clientSecret !== client_secret) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_client", error_description: "Client secret mismatch", success: false }
};
}
// grant_type几种类型 目前只支持authorization_code
if (grant_type !== "authorization_code") {
await context.commit();
return {
statusCode: 400,
data: { error: "unsupported_grant_type", error_description: "Only authorization_code grant type is supported", success: false }
};
}
// 找code的记录
const [authCodeRecord] = await context.select("oauthAuthorizationCode", {
data: {
id: 1,
code: 1,
redirectUri: 1,
userId: 1,
expiresAt: 1,
usedAt: 1,
codeChallenge: 1,
codeChallengeMethod: 1,
},
filter: {
code,
}
}, {});
// 找不到记录
if (!authCodeRecord) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Invalid authorization code", success: false }
};
}
// PKCE 验证
if (authCodeRecord.codeChallenge) {
if (!code_verifier) {
await context.commit();
return {
statusCode: 400,
data: {
error: "invalid_request",
error_description: "code_verifier is required",
success: false
}
};
}
// 验证 code_verifier
const isValid = await verifyPKCE(code_verifier, authCodeRecord.codeChallenge, authCodeRecord.codeChallengeMethod);
if (!isValid) {
await context.commit();
return {
statusCode: 400,
data: {
error: "invalid_grant",
error_description: "Invalid code_verifier",
success: false
}
};
}
}
// 验证redirect_uri
if (authCodeRecord.redirectUri !== redirect_uri) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Redirect URI mismatch", success: false }
};
}
// 验证过期
if (authCodeRecord.expiresAt < Date.now()) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Authorization code expired", success: false }
};
}
// 验证是否已使用
if (authCodeRecord.usedAt) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Authorization code already used", success: false }
};
}
// 如果userIdcode里面的appId都一样则说明是这个用户在这个应用下第二次授权的直接返回
const [existingToken] = await context.select("oauthToken", {
data: {
id: 1,
accessToken: 1,
refreshToken: 1,
accessExpiresAt: 1,
refreshExpiresAt: 1,
},
filter: {
userId: authCodeRecord.userId,
code: {
oauthAppId: app.id,
},
revokedAt: {
$exists: false,
}
}
}, {});
// 如果存在且未过期,直接返回
if (existingToken && (existingToken.accessExpiresAt > Date.now())) {
console.log("Existing valid token found, returning it directly");
// 刷新最后一次使用
await context.operate("oauthToken", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "update",
try {
const { client_id, client_secret, grant_type, code, redirect_uri, code_verifier // PKCE支持
} = body;
const [app] = await context.select("oauthApplication", {
data: {
lastUsedAt: Date.now(),
id: 1,
clientSecret: 1,
},
filter: {
id: existingToken.id,
id: client_id,
}
}, {});
// // 创建记录
// await context.operate("oauthUserAuthorization", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// tokenId: existingToken.id,
// usageState: 'granted',
// },
// filter: {
// codeId: authCodeRecord.id,
// }
// }, {})
await context.commit();
return {
statusCode: 200,
if (!app) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_client", error_description: "Client not found", success: false }
};
}
if (app.clientSecret !== client_secret) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_client", error_description: "Client secret mismatch", success: false }
};
}
// grant_type几种类型 目前只支持authorization_code
if (grant_type !== "authorization_code") {
await context.commit();
return {
statusCode: 400,
data: { error: "unsupported_grant_type", error_description: "Only authorization_code grant type is supported", success: false }
};
}
// 找code的记录
const [authCodeRecord] = await context.select("oauthAuthorizationCode", {
data: {
access_token: existingToken.accessToken,
token_type: "Bearer",
expires_in: existingToken.accessExpiresAt - Date.now(),
refresh_token: existingToken.refreshToken,
refresh_expires_in: existingToken.refreshExpiresAt - Date.now(),
success: true,
id: 1,
code: 1,
redirectUri: 1,
userId: 1,
expiresAt: 1,
usedAt: 1,
codeChallenge: 1,
codeChallengeMethod: 1,
},
filter: {
code,
}
};
}
const expiresIn = 3600; // 1 hour
const refreshTokenExpiresIn = 86400 * 30; // 30 days
// 如果过期就顺带刷新了(refresh token没过期 accessToken过期了)
if (existingToken && (existingToken.refreshExpiresAt > Date.now())) {
console.log("Existing token expired, refreshing it");
const newAccessToken = (0, crypto_1.randomUUID)();
const newRefreshToken = (0, crypto_1.randomUUID)();
}, {});
// 找不到记录
if (!authCodeRecord) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Invalid authorization code", success: false }
};
}
// PKCE 验证
if (authCodeRecord.codeChallenge) {
if (!code_verifier) {
await context.commit();
return {
statusCode: 400,
data: {
error: "invalid_request",
error_description: "code_verifier is required",
success: false
}
};
}
// 验证 code_verifier
const isValid = await verifyPKCE(code_verifier, authCodeRecord.codeChallenge, authCodeRecord.codeChallengeMethod);
if (!isValid) {
await context.commit();
return {
statusCode: 400,
data: {
error: "invalid_grant",
error_description: "Invalid code_verifier",
success: false
}
};
}
}
// 验证redirect_uri
if (authCodeRecord.redirectUri !== redirect_uri) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Redirect URI mismatch", success: false }
};
}
// 验证过期
if (authCodeRecord.expiresAt < Date.now()) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Authorization code expired", success: false }
};
}
// 验证是否已使用
if (authCodeRecord.usedAt) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Authorization code already used", success: false }
};
}
// 如果userIdcode里面的appId都一样则说明是这个用户在这个应用下第二次授权的直接返回
const [existingToken] = await context.select("oauthToken", {
data: {
id: 1,
accessToken: 1,
refreshToken: 1,
accessExpiresAt: 1,
refreshExpiresAt: 1,
},
filter: {
userId: authCodeRecord.userId,
code: {
oauthAppId: app.id,
},
revokedAt: {
$exists: false,
}
}
}, {});
// 如果存在且未过期,直接返回
if (existingToken && (existingToken.accessExpiresAt > Date.now())) {
console.log("Existing valid token found, returning it directly");
// 刷新最后一次使用
await context.operate("oauthToken", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "update",
data: {
lastUsedAt: Date.now(),
},
filter: {
id: existingToken.id,
}
}, {});
// // 创建记录
// await context.operate("oauthUserAuthorization", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// tokenId: existingToken.id,
// usageState: 'granted',
// },
// filter: {
// codeId: authCodeRecord.id,
// }
// }, {})
await context.commit();
return {
statusCode: 200,
data: {
access_token: existingToken.accessToken,
token_type: "Bearer",
expires_in: existingToken.accessExpiresAt - Date.now(),
refresh_token: existingToken.refreshToken,
refresh_expires_in: existingToken.refreshExpiresAt - Date.now(),
success: true,
}
};
}
const expiresIn = 3600; // 1 hour
const refreshTokenExpiresIn = 86400 * 30; // 30 days
// 如果过期就顺带刷新了(refresh token没过期 accessToken过期了)
if (existingToken && (existingToken.refreshExpiresAt > Date.now())) {
console.log("Existing token expired, refreshing it");
const newAccessToken = (0, crypto_1.randomUUID)();
const newRefreshToken = (0, crypto_1.randomUUID)();
await context.operate("oauthToken", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "update",
data: {
lastUsedAt: Date.now(),
accessToken: newAccessToken,
refreshToken: newRefreshToken,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
},
filter: {
id: existingToken.id,
}
}, {});
// // 创建记录
// await context.operate("oauthUserAuthorization", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// tokenId: existingToken.id,
// usageState: 'granted',
// },
// filter: {
// codeId: authCodeRecord.id,
// }
// }, {})
await context.commit();
return {
statusCode: 200,
data: {
access_token: newAccessToken,
token_type: "Bearer",
expires_in: expiresIn,
refresh_token: newRefreshToken,
refresh_expires_in: refreshTokenExpiresIn,
success: true,
}
};
}
// 有一种情况是access和refresh都过期了但是用户又重新用code来换token
// 这种情况下不能刷新老的,也要当作新的处理
// 创建accessToken
const genaccessToken = (0, crypto_1.randomUUID)();
const refreshToken = (0, crypto_1.randomUUID)();
console.log("Creating new access token and refresh token");
// create
const tokenId = await (0, uuid_1.generateNewIdAsync)();
await context.operate("oauthToken", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "create",
data: {
id: tokenId,
accessToken: genaccessToken,
refreshToken: refreshToken,
userId: authCodeRecord.userId,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
codeId: authCodeRecord.id,
}
}, {});
// 创建记录
await context.operate("oauthUserAuthorization", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "update",
data: {
lastUsedAt: Date.now(),
accessToken: newAccessToken,
refreshToken: newRefreshToken,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
tokenId: tokenId,
usageState: 'granted',
},
filter: {
codeId: authCodeRecord.id,
}
}, {});
// 标记code为已使用
await context.operate("oauthAuthorizationCode", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "update",
data: {
usedAt: Date.now(),
},
filter: {
id: authCodeRecord.id,
}
}, {});
// // 创建记录
// await context.operate("oauthUserAuthorization", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// tokenId: existingToken.id,
// usageState: 'granted',
// },
// filter: {
// codeId: authCodeRecord.id,
// }
// }, {})
await context.commit();
return {
statusCode: 200,
data: {
access_token: newAccessToken,
access_token: genaccessToken,
token_type: "Bearer",
expires_in: expiresIn,
refresh_token: newRefreshToken,
refresh_token: refreshToken,
refresh_expires_in: refreshTokenExpiresIn,
success: true,
}
};
}
// 有一种情况是access和refresh都过期了但是用户又重新用code来换token
// 这种情况下不能刷新老的,也要当作新的处理
// 创建accessToken
const genaccessToken = (0, crypto_1.randomUUID)();
const refreshToken = (0, crypto_1.randomUUID)();
console.log("Creating new access token and refresh token");
// create
const tokenId = await (0, uuid_1.generateNewIdAsync)();
await context.operate("oauthToken", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "create",
data: {
id: tokenId,
accessToken: genaccessToken,
refreshToken: refreshToken,
userId: authCodeRecord.userId,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
codeId: authCodeRecord.id,
}
}, {});
// 创建记录
await context.operate("oauthUserAuthorization", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "update",
data: {
tokenId: tokenId,
usageState: 'granted',
},
filter: {
codeId: authCodeRecord.id,
}
}, {});
// 标记code为已使用
await context.operate("oauthAuthorizationCode", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "update",
data: {
usedAt: Date.now(),
},
filter: {
id: authCodeRecord.id,
}
}, {});
await context.commit();
return {
statusCode: 200,
data: {
access_token: genaccessToken,
token_type: "Bearer",
expires_in: expiresIn,
refresh_token: refreshToken,
refresh_expires_in: refreshTokenExpiresIn,
success: true,
}
};
catch (err) {
console.error("Error in oauth token endpoint:", err);
await context.rollback();
return {
statusCode: 500,
data: { error: "server_error", error_description: "Internal server error: " + (err instanceof Error ? err.message : String(err)), success: false }
};
}
}
};
const oauthUserInfoEndpoint = {
@ -304,37 +317,47 @@ const oauthUserInfoEndpoint = {
fn: async (contextBuilder, params, header, req, body) => {
const context = await contextBuilder();
const token = header.authorization; // Bearer token
const checkResult = await (0, oauth_1.checkOauthTokenAvaliable)(context, token);
if (checkResult.error) {
try {
const checkResult = await (0, oauth_1.checkOauthTokenAvaliable)(context, token);
if (checkResult.error) {
await context.commit();
return {
statusCode: checkResult.statusCode || 401,
data: { error: checkResult.error, success: false }
};
}
const tokenRecord = checkResult.tokenRecord;
(0, assert_1.default)(tokenRecord?.user, "User must be present in token record");
(0, assert_1.default)(tokenRecord?.code?.application, "Application must be present in token record");
const extrafile = tokenRecord.user.extraFile$entity?.[0];
const application = tokenRecord.code.application;
let avatarUrl = '';
if (extrafile) {
avatarUrl = await (0, index_backend_1.composeFileUrlBackend)(application, extrafile, context);
}
await context.commit();
return {
statusCode: checkResult.statusCode || 401,
data: { error: checkResult.error, success: false }
statusCode: 200, data: {
userInfo: {
id: tokenRecord.user.id,
name: tokenRecord.user.name,
nickname: tokenRecord.user.nickname,
birth: tokenRecord.user.birth,
gender: tokenRecord.user.gender,
avatarUrl: avatarUrl,
},
error: null
}
};
}
const tokenRecord = checkResult.tokenRecord;
(0, assert_1.default)(tokenRecord?.user, "User must be present in token record");
(0, assert_1.default)(tokenRecord?.code?.application, "Application must be present in token record");
const extrafile = tokenRecord.user.extraFile$entity?.[0];
const application = tokenRecord.code.application;
let avatarUrl = '';
if (extrafile) {
avatarUrl = await (0, index_backend_1.composeFileUrlBackend)(application, extrafile, context);
catch (err) {
console.error("Error in oauth userinfo endpoint:", err);
await context.rollback();
return {
statusCode: 500,
data: { error: "server_error", error_description: "Internal server error: " + (err instanceof Error ? err.message : String(err)), success: false }
};
}
await context.commit();
return {
statusCode: 200, data: {
userInfo: {
id: tokenRecord.user.id,
name: tokenRecord.user.name,
nickname: tokenRecord.user.nickname,
birth: tokenRecord.user.birth,
gender: tokenRecord.user.gender,
avatarUrl: avatarUrl,
},
error: null
}
};
}
};
const refreshTokenEndpoint = {
@ -369,88 +392,98 @@ const refreshTokenEndpoint = {
};
}
const context = await contextBuilder();
const [oauthApp] = await context.select("oauthApplication", {
data: {
id: 1,
},
filter: {
clientSecret: client_secret,
id: client_id,
try {
const [oauthApp] = await context.select("oauthApplication", {
data: {
id: 1,
},
filter: {
clientSecret: client_secret,
id: client_id,
}
}, {});
if (!oauthApp) {
await context.commit();
return {
statusCode: 401,
data: { error: "invalid_client", error_description: "Client authentication failed", success: false }
};
}
}, {});
if (!oauthApp) {
await context.commit();
return {
statusCode: 401,
data: { error: "invalid_client", error_description: "Client authentication failed", success: false }
};
}
const [tokenRecord] = await context.select("oauthToken", {
data: {
id: 1,
userId: 1,
accessToken: 1,
accessExpiresAt: 1,
refreshToken: 1,
refreshExpiresAt: 1,
code: {
applicationId: 1,
oauthApp: {
id: 1,
const [tokenRecord] = await context.select("oauthToken", {
data: {
id: 1,
userId: 1,
accessToken: 1,
accessExpiresAt: 1,
refreshToken: 1,
refreshExpiresAt: 1,
code: {
applicationId: 1,
oauthApp: {
id: 1,
}
}
},
filter: {
refreshToken: refresh_token,
code: {
oauthAppId: oauthApp.id,
}
}
},
filter: {
refreshToken: refresh_token,
code: {
oauthAppId: oauthApp.id,
}, {});
if (!tokenRecord) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Invalid refresh token", success: false }
};
}
if (tokenRecord.refreshExpiresAt < Date.now()) {
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Refresh token expired", success: false }
};
}
// 生成新的令牌
const newAccessToken = (0, crypto_1.randomUUID)();
const newRefreshToken = (0, crypto_1.randomUUID)();
const expiresIn = 3600; // 1 hour
const refreshTokenExpiresIn = 86400 * 30; // 30 days
await context.operate("oauthToken", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "update",
data: {
accessToken: newAccessToken,
refreshToken: newRefreshToken,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
},
filter: {
id: tokenRecord.id,
}
}
}, {});
if (!tokenRecord) {
}, {});
await context.commit();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Invalid refresh token", success: false }
statusCode: 200,
data: {
access_token: newAccessToken,
token_type: "Bearer",
expires_in: expiresIn,
refresh_token: newRefreshToken,
refresh_expires_in: refreshTokenExpiresIn,
success: true,
}
};
}
if (tokenRecord.refreshExpiresAt < Date.now()) {
await context.commit();
catch (err) {
console.error("Error in refresh token endpoint:", err);
await context.rollback();
return {
statusCode: 400,
data: { error: "invalid_grant", error_description: "Refresh token expired", success: false }
statusCode: 500,
data: { error: "server_error", error_description: "Internal server error: " + (err instanceof Error ? err.message : String(err)), success: false }
};
}
// 生成新的令牌
const newAccessToken = (0, crypto_1.randomUUID)();
const newRefreshToken = (0, crypto_1.randomUUID)();
const expiresIn = 3600; // 1 hour
const refreshTokenExpiresIn = 86400 * 30; // 30 days
await context.operate("oauthToken", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "update",
data: {
accessToken: newAccessToken,
refreshToken: newRefreshToken,
accessExpiresAt: Date.now() + expiresIn * 1000,
refreshExpiresAt: Date.now() + refreshTokenExpiresIn * 1000,
},
filter: {
id: tokenRecord.id,
}
}, {});
await context.commit();
return {
statusCode: 200,
data: {
access_token: newAccessToken,
token_type: "Bearer",
expires_in: expiresIn,
refresh_token: newRefreshToken,
refresh_expires_in: refreshTokenExpiresIn,
success: true,
}
};
}
};
const oauthRevocationEndpoint = {
@ -478,64 +511,74 @@ const oauthRevocationEndpoint = {
};
}
const context = await contextBuilder();
const [oauthApp] = await context.select("oauthApplication", {
data: { id: 1 },
filter: { clientSecret: client_secret, id: client_id }
}, {});
if (!oauthApp) {
try {
const [oauthApp] = await context.select("oauthApplication", {
data: { id: 1 },
filter: { clientSecret: client_secret, id: client_id }
}, {});
if (!oauthApp) {
await context.commit();
return {
statusCode: 401,
data: { error: "invalid_client", error_description: "Client authentication failed", success: false }
};
}
// 3. 查找令牌记录
let tokenRecord = null;
const tokenProjection = {
data: { id: 1, code: { oauthAppId: 1 } },
filter: {}
};
// 尝试查找 Refresh Token
if (!token_type_hint || token_type_hint === 'refresh_token') {
tokenProjection.filter = { refreshToken: token, code: { oauthAppId: oauthApp.id } };
[tokenRecord] = await context.select("oauthToken", tokenProjection, {});
}
// 如果没找到,且 hint 不是 'refresh_token',则尝试查找 Access Token
if (!tokenRecord && (!token_type_hint || token_type_hint === 'access_token')) {
tokenProjection.filter = { accessToken: token, code: { oauthAppId: oauthApp.id } };
[tokenRecord] = await context.select("oauthToken", tokenProjection, {});
}
// 4. 撤销操作(无论找到与否,都返回 200但如果找到则执行失效操作
if (tokenRecord) {
// const pastTime = Date.now() - 1000;
// // 将 Access Token 和 Refresh Token 的过期时间都设为过去,使其立即失效
// await context.operate("oauthToken", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// accessExpiresAt: pastTime,
// refreshExpiresAt: pastTime,
// },
// filter: {
// id: tokenRecord.id,
// }
// }, {});
// 使用这个token的认证记录都撤销掉在trigger里会自动设置 revokedAt
await context.operate("oauthUserAuthorization", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "revoke",
data: {},
filter: {
tokenId: tokenRecord.id,
}
}, {});
}
await context.commit();
// 5. RFC 7009 规定:令牌撤销成功或令牌无效时,返回 HTTP 200
return {
statusCode: 401,
data: { error: "invalid_client", error_description: "Client authentication failed", success: false }
statusCode: 200,
data: {}
};
}
// 3. 查找令牌记录
let tokenRecord = null;
const tokenProjection = {
data: { id: 1, code: { oauthAppId: 1 } },
filter: {}
};
// 尝试查找 Refresh Token
if (!token_type_hint || token_type_hint === 'refresh_token') {
tokenProjection.filter = { refreshToken: token, code: { oauthAppId: oauthApp.id } };
[tokenRecord] = await context.select("oauthToken", tokenProjection, {});
catch (err) {
console.error("Error in oauth token revocation endpoint:", err);
await context.rollback();
return {
statusCode: 500,
data: { error: "server_error", error_description: "Internal server error: " + (err instanceof Error ? err.message : String(err)), success: false }
};
}
// 如果没找到,且 hint 不是 'refresh_token',则尝试查找 Access Token
if (!tokenRecord && (!token_type_hint || token_type_hint === 'access_token')) {
tokenProjection.filter = { accessToken: token, code: { oauthAppId: oauthApp.id } };
[tokenRecord] = await context.select("oauthToken", tokenProjection, {});
}
// 4. 撤销操作(无论找到与否,都返回 200但如果找到则执行失效操作
if (tokenRecord) {
// const pastTime = Date.now() - 1000;
// // 将 Access Token 和 Refresh Token 的过期时间都设为过去,使其立即失效
// await context.operate("oauthToken", {
// id: await generateNewIdAsync(),
// action: "update",
// data: {
// accessExpiresAt: pastTime,
// refreshExpiresAt: pastTime,
// },
// filter: {
// id: tokenRecord.id,
// }
// }, {});
// 使用这个token的认证记录都撤销掉在trigger里会自动设置 revokedAt
await context.operate("oauthUserAuthorization", {
id: await (0, uuid_1.generateNewIdAsync)(),
action: "revoke",
data: {},
filter: {
tokenId: tokenRecord.id,
}
}, {});
}
await context.commit();
// 5. RFC 7009 规定:令牌撤销成功或令牌无效时,返回 HTTP 200
return {
statusCode: 200,
data: {}
};
}
};
const endpoints = {

View File

@ -7,9 +7,6 @@ const Projection_1 = require("../../types/Projection");
const handler_1 = require("./handler");
const handlerMap = new Map();
const registerOauthUserinfoHandler = (type, handler) => {
if (handlerMap.has(type)) {
throw new Error(`oauth provider type ${type} 的 userinfo 处理器已注册`);
}
handlerMap.set(type, handler);
};
exports.registerOauthUserinfoHandler = registerOauthUserinfoHandler;

File diff suppressed because it is too large Load Diff

View File

@ -18,9 +18,6 @@ export type UserInfoHandler = (data: UserInfo) => UserID | Promise<UserID>
const handlerMap = new Map<EntityDict['oauthProvider']['Schema']['type'], UserInfoHandler>();
export const registerOauthUserinfoHandler = (type: EntityDict['oauthProvider']['Schema']['type'], handler: UserInfoHandler) => {
if (handlerMap.has(type)) {
throw new Error(`oauth provider type ${type} 的 userinfo 处理器已注册`);
}
handlerMap.set(type, handler);
}
@ -38,7 +35,6 @@ Object.entries(defaulthandlers).forEach(([type, handler]) => {
registerOauthUserinfoHandler(type as EntityDict['oauthProvider']['Schema']['type'], handler);
});
function validateToken(token: string | undefined): {
token: string;
error: string | null;