From beb7a4c99277262bb9681c5594cfcd3e36c52074 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Thu, 5 Feb 2026 16:14:05 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E7=AC=AC=E4=B8=89=E6=96=B9=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=94=AF=E6=8C=81Microsoft?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin-cert/plugin/cert-plugin/acme.ts | 2 +- .../src/plugins/plugin-oauth/index.ts | 3 +- .../plugin-oauth/oauth2/plugin-microsoft.ts | 122 ++++++++++++++++++ 3 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 packages/ui/certd-server/src/plugins/plugin-oauth/oauth2/plugin-microsoft.ts diff --git a/packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/acme.ts b/packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/acme.ts index 80b6502cf..6bdb7cf51 100644 --- a/packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/acme.ts +++ b/packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/acme.ts @@ -147,7 +147,7 @@ export class AcmeService { externalAccountBinding: this.eab, backoffAttempts: this.options.maxCheckRetryCount || 20, backoffMin: 5000, - backoffMax: 400000, + backoffMax: 1000*1000, urlMapping, signal: this.options.signal, logger: this.logger, diff --git a/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts b/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts index 3a43d4351..37157bcd7 100644 --- a/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts @@ -4,4 +4,5 @@ export * from './wx/plugin-wx.js' export * from './oauth2/plugin-gitee.js' export * from './oauth2/plugin-clogin.js' export * from './oauth2/plugin-github.js' -export * from './oauth2/plugin-google.js' \ No newline at end of file +export * from './oauth2/plugin-google.js' +export * from './oauth2/plugin-microsoft.js' \ No newline at end of file diff --git a/packages/ui/certd-server/src/plugins/plugin-oauth/oauth2/plugin-microsoft.ts b/packages/ui/certd-server/src/plugins/plugin-oauth/oauth2/plugin-microsoft.ts new file mode 100644 index 000000000..e42b9b6c1 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-oauth/oauth2/plugin-microsoft.ts @@ -0,0 +1,122 @@ +import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server"; +import { BuildLoginUrlReq, BuildLogoutUrlReq, IOauthProvider, OnCallbackReq } from "../api.js"; + +@IsAddon({ + addonType: "oauth", + name: 'microsoft', + title: 'Microsoft认证', + desc: 'Microsoft OAuth2登录', + icon:"simple-icons:microsoft", + showTest: false, +}) +export class MicrosoftOauthProvider extends BaseAddon implements IOauthProvider { + + @AddonInput({ + title: "ClientId", + helper: "[Azure Portal](https://portal.azure.com/)创建应用后获取", + required: true, + }) + clientId = ""; + + @AddonInput({ + title: "ClientSecretKey", + component: { + placeholder: "ClientSecretKey / appSecretKey", + }, + required: true, + }) + clientSecretKey = ""; + + @AddonInput({ + title: "TenantId", + helper: "租户ID,留空使用/common端点(需要应用配置为多租户)", + component: { + placeholder: "common 或 租户ID", + }, + value: "common", + required: false, + }) + tenantId = "common"; + + async buildLoginUrl(params: BuildLoginUrlReq) { + + let scope = "openid profile email User.Read" // Scope of the access request + let state:any = { + forType: params.forType || 'login', + } + state = this.ctx.utils.hash.base64(JSON.stringify(state)) + + const authorizeEndpoint = `https://login.microsoftonline.com/${this.tenantId}/oauth2/v2.0/authorize` + const redirectUrl = encodeURIComponent(params.redirectUri) + const loginUrl = `${authorizeEndpoint}?client_id=${this.clientId}&redirect_uri=${redirectUrl}&response_type=code&scope=${scope}&state=${state}` + return { + loginUrl, + ticketValue: { + state, + }, + }; + } + + async onCallback(req: OnCallbackReq) { + + const code = req.code || "" + if (!code) { + throw new Error("Missing code parameter"); + } + + const tokenEndpoint = `https://login.microsoftonline.com/${this.tenantId}/oauth2/v2.0/token` + + const uri = new URL(req.currentURL) + const redirectUri = `${uri.origin}${uri.pathname}` + + // 构建 form-urlencoded 格式的数据 + const formData = new URLSearchParams(); + formData.append('client_id', this.clientId); + formData.append('client_secret', this.clientSecretKey); + formData.append('code', code); + formData.append('redirect_uri', redirectUri); + formData.append('grant_type', 'authorization_code'); + + const res = await this.ctx.utils.http.request( { + url: tokenEndpoint, + method: "post", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + "Accept": "application/json" + }, + data: formData.toString() + }) + + const tokens = res + + const userInfoEndpoint = "https://graph.microsoft.com/v1.0/me" + + // 获取用户信息 + const userInfoRes = await this.ctx.utils.http.request( { + url: userInfoEndpoint, + method: "get", + headers: { + "Authorization": `Bearer ${tokens.access_token}`, + "Accept": "application/json" + } + }) + const userInfo = userInfoRes + + return { + token:{ + accessToken: tokens.access_token, + refreshToken: tokens.refresh_token, + expiresIn: tokens.expires_in, + }, + userInfo: { + openId: userInfo.id, + nickName: userInfo.displayName || userInfo.userPrincipalName || "", + avatar: userInfo.avatar || "", + }, + } + }; + + async buildLogoutUrl(params: BuildLogoutUrlReq) { + return {}; + } +} \ No newline at end of file