/* eslint-disable @typescript-eslint/no-useless-constructor */ /** * v1 兼容层 * 本文件包含 v1 版本的 Provider 类、Auth v1 兼容方法和 User v1 兼容方法。 * Auth 类通过 extends AuthV1Compat 来继承 v1 方法。 * User 类通过 applyUserV1Compat mixin 来扩展 v1 方法。 */ import type { authModels } from '@cloudbase/oauth' import { LOGIN_STATE_CHANGED_TYPE, AUTH_STATE_CHANGED_TYPE, } from '@cloudbase/oauth' import { throwError, ERRORS, } from './utilities' import type { SignUpRes } from './type' // ========== v1 兼容 Provider 类 ========== /** * v1 微信登录 Provider * 使用方式: auth.weixinAuthProvider({ appid, scope }).signInWithRedirect() * * @deprecated v1 兼容 API。建议使用 auth.signInWithOAuth({ provider }) 替代。 */ export class WeixinAuthProvider { constructor(_options: { authInstance: any appid: string scope: string }) { // v1 兼容类,所有方法均抛出异常提示替代方案 } /** * @deprecated v1 兼容 API。 * 建议使用 auth.signInWithOAuth({ provider: 'providerId' }) 替代。 */ public signInWithRedirect(): void { throw new Error('[v1 兼容] WeixinAuthProvider.signInWithRedirect() 在当前版本中无法实现。' + ' 建议使用 auth.signInWithOAuth({ provider: \'providerId\' }) 替代。',) } /** * @deprecated v1 兼容 API。 * 建议使用 auth.verifyOAuth({ code, state, provider }) 替代。 */ public async getRedirectResult(_options?: { createUser?: boolean syncUserInfo?: boolean }): Promise { throw new Error('[v1 兼容] WeixinAuthProvider.getRedirectResult() 在当前版本中无法实现。' + ' 建议使用 auth.verifyOAuth({ code, state, provider }) 替代。',) } /** * @deprecated v1 兼容 API。 * 建议使用 auth.linkIdentity({ provider: 'providerId' }) 替代。 */ public async getLinkRedirectResult(_options?: { withUnionId?: boolean }): Promise { throw new Error('[v1 兼容] WeixinAuthProvider.getLinkRedirectResult() 在当前版本中无法实现。' + ' 建议使用 auth.linkIdentity({ provider: \'providerId\' }) 替代。',) } } /** * v1 自定义登录 Provider * 使用方式: auth.customAuthProvider().signIn(ticket) * * @deprecated v1 兼容 API。建议使用 auth.signInWithCustomTicket(() => Promise.resolve(ticket)) 替代。 */ export class CustomAuthProvider { private authInstance: any constructor(options: { authInstance: any }) { this.authInstance = options.authInstance } /** * 使用自定义登录凭据 ticket 登录云开发 * @param ticket 自定义登录 ticket * @deprecated v1 兼容 API。建议使用 auth.signInWithCustomTicket(() => Promise.resolve(ticket)) 替代。 */ public async signIn(ticket: string): Promise { // 使用 Auth 已有的 signInWithCustomTicket 方法 await this.authInstance.signInWithCustomTicket(() => Promise.resolve(ticket)) } } /** * v1 匿名登录 Provider * 使用方式: auth.anonymousAuthProvider().signIn() * * @deprecated v1 兼容 API。建议使用 auth.signInAnonymously({}) 替代。 */ export class AnonymousAuthProvider { private authInstance: any constructor(options: { authInstance: any }) { this.authInstance = options.authInstance } /** * 匿名登录云开发 * @deprecated v1 兼容 API。建议使用 auth.signInAnonymously({}) 替代。 */ public async signIn(): Promise { // 使用 Auth 已有的 signInAnonymously 方法 await this.authInstance.signInAnonymously({}) } } // ========== Auth v1 兼容基类 ========== /** * AuthV1Compat 基类,提供 v1 版本的兼容 API 方法。 * Auth 类通过 extends AuthV1Compat 来继承这些方法。 * * 注意:此类中的方法通过 this 访问子类 Auth 的方法和属性, * 包括: signIn, signUp, signInWithCustomTicket, signInAnonymously, * getVerification, resetPassword, onLoginStateChanged, onAuthStateChange, * createLoginState, formatPhone, config, signInWithOAuth 等。 */ // eslint-disable @typescript-eslint/member-ordering -- abstract 基类中 abstract 声明与 public 方法排序不适用常规规则 export abstract class AuthV1Compat { abstract readonly config: any // ========== v1 兼容 API 方法 ========== /** * v1 API: 获取用于微信登录的 WeixinAuthProvider // cspell:ignore weixin Weixin * @deprecated 建议使用 auth.signInWithOAuth({ provider: appid }) 替代。 */ public weixinAuthProvider(options: { appid: string; scope: string }): WeixinAuthProvider { return new WeixinAuthProvider({ authInstance: this, appid: options.appid, scope: options.scope, }) } /** * v1 API: 获取用于自定义登录的 CustomAuthProvider * @deprecated 建议使用 auth.signInWithCustomTicket(() => Promise.resolve(ticket)) 替代。 */ public customAuthProvider(): CustomAuthProvider { return new CustomAuthProvider({ authInstance: this, }) } /** * v1 API: 获取用于匿名登录的 AnonymousAuthProvider * @deprecated 建议使用 auth.signInAnonymously({}) 替代。 */ public anonymousAuthProvider(): AnonymousAuthProvider { return new AnonymousAuthProvider({ authInstance: this, }) } /** * v1 API: 使用邮箱和密码注册云开发账户 * @deprecated 建议使用 auth.signUp({ email, password }) 替代。 */ public async signUpWithEmailAndPassword(email: string, password: string): Promise { if (typeof email !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'email must be a string') } if (typeof password !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'password must be a string') } // 使用 Auth 已有的 signUp 方法 return await this.signUp({ email, password, }) } /** * v1 API: 使用邮箱和密码登录云开发 * @deprecated 建议使用 auth.signInWithPassword({ email, password }) 替代。 */ public async signInWithEmailAndPassword(email: string, password: string): Promise { if (typeof email !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'email must be a string') } if (typeof password !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'password must be a string') } // 使用 Auth 已有的 signIn 方法 await this.signIn({ username: email, password, }) return this.createLoginState() } /** * v1 API: 发送重置密码的邮件 * @deprecated 建议使用 auth.resetPasswordForEmail(email) 替代。 */ public async sendPasswordResetEmail(email: string): Promise { if (typeof email !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'email must be a string') } // 使用 Auth 已有的 getVerification 方法 await this.getVerification({ email }) } /** * v1 API: 使用用户名密码登录云开发 * @deprecated 建议使用 auth.signInWithPassword({ username, password }) 替代。 */ public async signInWithUsernameAndPassword(username: string, password: string): Promise { if (typeof username !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'username must be a string') } if (typeof password !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'password must be a string') } // 使用 Auth 已有的 signIn 方法 await this.signIn({ username, password, }) return this.createLoginState() } /** * v1 API: 发送手机验证码 * @deprecated 建议使用 auth.signInWithOtp({ phone }) 或 auth.getVerification({ phone_number }) 替代。 */ public async sendPhoneCode(phoneNumber: string): Promise { if (typeof phoneNumber !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'phoneNumber must be a string') } // 使用 Auth 已有的 getVerification 方法 const formattedPhone = this.formatPhone(phoneNumber) await this.getVerification({ phone_number: formattedPhone }) return true } /** * v1 API: 手机号注册(支持短信验证码+密码方式) * @deprecated 建议使用 auth.signUp({ phone_number, verification_code, password? }) 替代。 */ public async signUpWithPhoneCode(phoneNumber: string, phoneCode: string, password?: string): Promise { if (typeof phoneNumber !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'phoneNumber must be a string') } if (typeof phoneCode !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'phoneCode must be a string') } const formattedPhone = this.formatPhone(phoneNumber) // 使用 Auth 已有的 signUp 方法 await this.signUp({ phone_number: formattedPhone, verification_code: phoneCode, ...(password ? { password } : {}), }) return this.createLoginState() } /** * v1 API: 手机号登录(支持短信验证码 or 密码方式) * @deprecated 密码方式建议使用 auth.signInWithPassword({ phone, password }) 替代; * 验证码方式建议使用 auth.signInWithOtp({ phone }) 替代。 */ public async signInWithPhoneCodeOrPassword(params: { phoneNumber: string phoneCode?: string password?: string }): Promise { const { phoneNumber, phoneCode, password } = params if (typeof phoneNumber !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'phoneNumber must be a string') } const formattedPhone = this.formatPhone(phoneNumber) if (password) { // 使用 Auth 已有的 signIn 方法进行密码登录 await this.signIn({ username: formattedPhone, password, }) } else if (phoneCode) { // 使用 Auth 已有的 signIn 方法进行验证码登录 await this.signIn({ username: formattedPhone, verification_token: phoneCode, } as any) } else { throwError(ERRORS.INVALID_PARAMS, 'phoneCode or password must be provided') } return this.createLoginState() } /** * v1 API: 手机号强制重置密码 * @deprecated 建议使用 auth.resetPasswordForEmail(phoneNumber) 替代。 */ public async forceResetPwdByPhoneCode(params: { phoneNumber: string phoneCode: string password: string }): Promise { const { phoneNumber, phoneCode, password } = params if (typeof phoneNumber !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'phoneNumber must be a string') } if (typeof phoneCode !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'phoneCode must be a string') } if (typeof password !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'password must be a string') } const formattedPhone = this.formatPhone(phoneNumber) // 使用 Auth 已有的 resetPassword 方法 await this.resetPassword({ phone_number: formattedPhone, new_password: password, verification_token: phoneCode, }) // 重置密码成功后使用 Auth 已有的 signIn 方法自动登录 await this.signIn({ username: formattedPhone, password, }) return this.createLoginState() } /** * v1 API: 接收一个回调函数,在刷新短期访问令牌前调用,根据返回值决定是否刷新 * @deprecated v1 兼容 API,当前 v3 SDK 中无直接对应方法。 * 建议使用 auth.onAuthStateChange(callback) 监听 TOKEN_REFRESHED 事件替代。 */ public shouldRefreshAccessToken(_callback: () => boolean): void { throw new Error('[v1 兼容] shouldRefreshAccessToken() 在当前版本中无法实现。' + ' 建议使用 auth.onAuthStateChange(callback) 来监听 TOKEN_REFRESHED 事件替代。',) } /** * v1 API: 接收一个回调函数,在登录状态过期时调用 * @deprecated 建议使用 auth.onAuthStateChange(callback) 替代,监听 SIGNED_OUT 事件。 */ public onLoginStateExpired(callback: Function): void { // 使用 Auth 已有的 onLoginStateChanged 方法 this.onLoginStateChanged((params) => { if (params?.eventType === LOGIN_STATE_CHANGED_TYPE.CREDENTIALS_ERROR) { callback.call(this) } }) } /** * v1 API: 接收一个回调函数,在短期访问令牌刷新后调用 * @deprecated 建议使用 auth.onAuthStateChange(callback) 替代,监听 TOKEN_REFRESHED 事件。 */ public onAccessTokenRefreshed(callback: Function): void { // 使用 Auth 已有的 onAuthStateChange 方法 this.onAuthStateChange((event) => { if (event === AUTH_STATE_CHANGED_TYPE.TOKEN_REFRESHED) { callback.call(this) } }) } /** * v1 API: 接收一个回调函数,在匿名登录状态被转换后调用 * @deprecated 建议使用 auth.onAuthStateChange(callback) 替代,监听 SIGNED_IN 事件。 */ public onAnonymousConverted(callback: Function): void { // 使用 Auth 已有的 onAuthStateChange 方法 this.onAuthStateChange((event) => { if (event === AUTH_STATE_CHANGED_TYPE.SIGNED_IN) { callback.call(this) } }) } /** * v1 API: 接收一个回调函数,在登录类型发生变化后调用 * @deprecated 建议使用 auth.onAuthStateChange(callback) 替代,监听 SIGNED_IN / SIGNED_OUT 事件。 */ public onLoginTypeChanged(callback: Function): void { // 使用 Auth 已有的 onAuthStateChange 方法 this.onAuthStateChange((event) => { if (event === AUTH_STATE_CHANGED_TYPE.SIGNED_IN || event === AUTH_STATE_CHANGED_TYPE.SIGNED_OUT) { callback.call(this) } }) } // ========== 声明子类 Auth 需要提供的方法/属性 ========== abstract signIn(params: authModels.SignInRequest): Promise abstract signUp(params: authModels.SignUpRequest & { phone?: string }): Promise abstract signInWithCustomTicket(getTickFn?: authModels.GetCustomSignTicketFn): Promise abstract signInAnonymously(params: any): Promise abstract getVerification(params: any, options?: any): Promise abstract resetPassword(params: authModels.ResetPasswordRequest): Promise abstract onLoginStateChanged(callback: Function): Promise abstract onAuthStateChange(callback: any): any abstract createLoginState(params?: any, options?: any): Promise abstract signInWithOAuth(params: any): Promise abstract grantProviderToken(params: authModels.GrantProviderTokenRequest): Promise abstract bindWithProvider(params: authModels.BindWithProviderRequest): Promise protected abstract formatPhone(phone: string): string } // ========== User v1 兼容 mixin ========== /** * 将 v1 兼容方法应用到 User 类的 prototype 上。 * 在 index.ts 中调用: applyUserV1Compat(User) */ export function applyUserV1Compat(UserClass: any): void { /** * v1 API: 将当前账户与自定义登录 Ticket 进行绑定 * @deprecated v1 兼容 API,当前版本中无法直接实现。 * 建议使用 auth.signInWithCustomTicket(() => Promise.resolve(ticket)) 替代。 */ UserClass.prototype.linkWithTicket = async function (_ticket: string): Promise { throw new Error('[v1 兼容] User.linkWithTicket() 在当前版本中无法实现。' + ' 建议使用 auth.signInWithCustomTicket(() => Promise.resolve(ticket)) 替代。',) } /** * v1 API: 将当前账户与第三方鉴权提供方(以重定向形式)进行绑定 * @deprecated 建议使用 auth.linkIdentity({ provider: 'providerId' }) 替代。 */ UserClass.prototype.linkWithRedirect = function (_provider: any): void { throw new Error('[v1 兼容] User.linkWithRedirect() 在当前版本中无法实现。' + ' 建议使用 auth.linkIdentity({ provider: \'providerId\' }) 替代。',) } /** * v1 API: 更新当前的登录邮箱 * @deprecated 建议使用 auth.updateUser({ email: newEmail }) 替代。 */ UserClass.prototype.updateEmail = async function (newEmail: string, _password?: string): Promise { if (typeof newEmail !== 'string') { throwError(ERRORS.INVALID_PARAMS, 'newEmail must be a string') } // 使用 User 已有的 updateUserBasicInfo 方法 await this.updateUserBasicInfo({ email: newEmail, }) } /** * v1 API: 绑定手机号 * @deprecated v1 兼容 API,当前版本中无法直接在 User 上实现。 * 建议使用 auth.bindPhoneNumber({ phone_number, verification_token, sudo_token }) 替代。 */ UserClass.prototype.linkWithPhoneNumber = async function (_phoneNumber: string, _phoneCode: string): Promise { throw new Error('[v1 兼容] User.linkWithPhoneNumber() 在当前版本中无法实现。' + ' 建议使用 auth.bindPhoneNumber({ phone_number, verification_token, sudo_token }) 替代。',) } /** * v1 API: 更新手机号 * @deprecated v1 兼容 API,当前版本中无法直接在 User 上实现。 * 建议使用 auth.updateUser({ phone: newPhoneNumber }) 替代。 */ UserClass.prototype.updatePhoneNumber = async function (_phoneNumber: string, _phoneCode: string): Promise { throw new Error('[v1 兼容] User.updatePhoneNumber() 在当前版本中无法实现。' + ' 建议使用 auth.updateUser({ phone: newPhoneNumber }) 替代。',) } /** * v1 API: 解绑某个登录方式 * @deprecated v1 兼容 API,当前版本中无法直接在 User 上实现。 * 建议使用 auth.unlinkIdentity({ provider: loginType }) 替代。 */ UserClass.prototype.unlink = async function (_loginType: string): Promise { throw new Error('[v1 兼容] User.unlink() 在当前版本中无法实现。' + ' 建议使用 auth.unlinkIdentity({ provider: loginType }) 替代。',) } }