feat(server): OIDC 登录支持(F23)
- 引入 openid-client,新增 /api/auth/oidc/login + /api/auth/oidc/callback - 通过 email 绑定既有员工账号,保留密码登录 fallback - OIDC 状态存 cookie(HMAC) + PKCE + SameSite=lax 防 CSRF - 更新 env/README 与 v3 CSV 状态推进
这个提交包含在:
@@ -0,0 +1,64 @@
|
||||
const { Issuer, generators } = require("openid-client");
|
||||
|
||||
function validateOidcConfig(config) {
|
||||
if (!config?.oidcEnabled) {
|
||||
throw new Error("OIDC is disabled");
|
||||
}
|
||||
|
||||
if (!config.oidcIssuerUrl) {
|
||||
throw new Error("Missing OIDC_ISSUER_URL");
|
||||
}
|
||||
|
||||
if (!config.oidcClientId) {
|
||||
throw new Error("Missing OIDC_CLIENT_ID");
|
||||
}
|
||||
|
||||
if (!config.oidcRedirectUri) {
|
||||
throw new Error("Missing OIDC_REDIRECT_URI");
|
||||
}
|
||||
|
||||
return {
|
||||
issuerUrl: config.oidcIssuerUrl,
|
||||
clientId: config.oidcClientId,
|
||||
clientSecret: config.oidcClientSecret || undefined,
|
||||
redirectUri: config.oidcRedirectUri,
|
||||
scopes: config.oidcScopes || "openid profile email",
|
||||
};
|
||||
}
|
||||
|
||||
let cachedClientPromise = null;
|
||||
let cachedClientKey = null;
|
||||
|
||||
async function getOidcClient(config) {
|
||||
const oidc = validateOidcConfig(config);
|
||||
const key = JSON.stringify({
|
||||
issuerUrl: oidc.issuerUrl,
|
||||
clientId: oidc.clientId,
|
||||
redirectUri: oidc.redirectUri,
|
||||
});
|
||||
|
||||
if (cachedClientPromise && cachedClientKey === key) {
|
||||
return cachedClientPromise;
|
||||
}
|
||||
|
||||
cachedClientKey = key;
|
||||
cachedClientPromise = Issuer.discover(oidc.issuerUrl).then((issuer) => {
|
||||
const client = new issuer.Client({
|
||||
client_id: oidc.clientId,
|
||||
client_secret: oidc.clientSecret,
|
||||
redirect_uris: [oidc.redirectUri],
|
||||
response_types: ["code"],
|
||||
});
|
||||
|
||||
return { issuer, client, config: oidc };
|
||||
});
|
||||
|
||||
return cachedClientPromise;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generators,
|
||||
getOidcClient,
|
||||
validateOidcConfig,
|
||||
};
|
||||
|
||||
在新工单中引用
屏蔽一个用户