fix: 修复oauth-endpoint相关问题,并允许重复注册处理器
This commit is contained in:
parent
ba434da707
commit
a3ec5fc808
|
|
@ -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 }
|
||||
};
|
||||
}
|
||||
// 如果userId,code里面的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 }
|
||||
};
|
||||
}
|
||||
// 如果userId,code里面的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 = {
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
};
|
||||
}
|
||||
// 如果userId,code里面的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 }
|
||||
};
|
||||
}
|
||||
// 如果userId,code里面的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 = {
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue