Support TOTP two-factor authentication for frontend

Signed-off-by: John Niang <johnniang@foxmail.com>
pull/4737/head
Ryan Wang 2024-01-15 15:21:27 +08:00 committed by John Niang
parent 6d49047408
commit 5fab8aca5a
22 changed files with 1673 additions and 57 deletions

View File

@ -71,6 +71,7 @@
"@uppy/xhr-upload": "^3.6.0",
"@vueuse/components": "^10.3.0",
"@vueuse/core": "^10.3.0",
"@vueuse/integrations": "^10.5.0",
"@vueuse/router": "^10.3.0",
"@vueuse/shared": "^10.3.0",
"axios": "^0.27.2",
@ -89,6 +90,7 @@
"path-browserify": "^1.0.1",
"pinia": "^2.1.6",
"pretty-bytes": "^6.0.0",
"qrcode": "^1.5.3",
"qs": "^6.11.1",
"short-unique-id": "^5.0.2",
"transliteration": "^2.3.5",

View File

@ -29,6 +29,7 @@ api/api-notification-halo-run-v1alpha1-notification-api.ts
api/api-notification-halo-run-v1alpha1-notifier-api.ts
api/api-notification-halo-run-v1alpha1-subscription-api.ts
api/api-plugin-halo-run-v1alpha1-plugin-api.ts
api/api-security-halo-run-v1alpha1-authentication-two-factor-api.ts
api/api-security-halo-run-v1alpha1-personal-access-token-api.ts
api/auth-halo-run-v1alpha1-auth-provider-api.ts
api/auth-halo-run-v1alpha1-user-connection-api.ts
@ -186,6 +187,7 @@ models/notifier-descriptor.ts
models/notifier-info.ts
models/notifier-setting-ref.ts
models/owner-info.ts
models/password-request.ts
models/password-reset-email-request.ts
models/pat-spec.ts
models/personal-access-token-list.ts
@ -283,6 +285,9 @@ models/theme-list.ts
models/theme-spec.ts
models/theme-status.ts
models/theme.ts
models/totp-auth-link-response.ts
models/totp-request.ts
models/two-factor-auth-settings.ts
models/upgrade-from-uri-request.ts
models/user-connection-list.ts
models/user-connection-spec.ts

View File

@ -40,6 +40,7 @@ export * from "./api/api-notification-halo-run-v1alpha1-notification-api";
export * from "./api/api-notification-halo-run-v1alpha1-notifier-api";
export * from "./api/api-notification-halo-run-v1alpha1-subscription-api";
export * from "./api/api-plugin-halo-run-v1alpha1-plugin-api";
export * from "./api/api-security-halo-run-v1alpha1-authentication-two-factor-api";
export * from "./api/api-security-halo-run-v1alpha1-personal-access-token-api";
export * from "./api/auth-halo-run-v1alpha1-auth-provider-api";
export * from "./api/auth-halo-run-v1alpha1-user-connection-api";

View File

@ -0,0 +1,795 @@
/* tslint:disable */
/* eslint-disable */
/**
* Halo Next API
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 2.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import type { Configuration } from "../configuration";
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios";
import globalAxios from "axios";
// Some imports not used depending on template conditions
// @ts-ignore
import {
DUMMY_BASE_URL,
assertParamExists,
setApiKeyToObject,
setBasicAuthToObject,
setBearerAuthToObject,
setOAuthToObject,
setSearchParams,
serializeDataIfNeeded,
toPathString,
createRequestFunction,
} from "../common";
// @ts-ignore
import {
BASE_PATH,
COLLECTION_FORMATS,
RequestArgs,
BaseAPI,
RequiredError,
} from "../base";
// @ts-ignore
import { PasswordRequest } from "../models";
// @ts-ignore
import { TotpAuthLinkResponse } from "../models";
// @ts-ignore
import { TotpRequest } from "../models";
// @ts-ignore
import { TwoFactorAuthSettings } from "../models";
/**
* ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi - axios parameter creator
* @export
*/
export const ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiAxiosParamCreator =
function (configuration?: Configuration) {
return {
/**
* Configure a TOTP
* @param {TotpRequest} [totpRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
configurerTotp: async (
totpRequest?: TotpRequest,
options: AxiosRequestConfig = {}
): Promise<RequestArgs> => {
const localVarPath = `/apis/api.security.halo.run/v1alpha1/authentications/two-factor/totp`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = {
method: "POST",
...baseOptions,
...options,
};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication BasicAuth required
// http basic authentication required
setBasicAuthToObject(localVarRequestOptions, configuration);
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration);
localVarHeaderParameter["Content-Type"] = "application/json";
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions =
baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {
...localVarHeaderParameter,
...headersFromBaseOptions,
...options.headers,
};
localVarRequestOptions.data = serializeDataIfNeeded(
totpRequest,
localVarRequestOptions,
configuration
);
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {PasswordRequest} [passwordRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
deleteTotp: async (
passwordRequest?: PasswordRequest,
options: AxiosRequestConfig = {}
): Promise<RequestArgs> => {
const localVarPath = `/apis/api.security.halo.run/v1alpha1/authentications/two-factor/totp/-`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = {
method: "DELETE",
...baseOptions,
...options,
};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication BasicAuth required
// http basic authentication required
setBasicAuthToObject(localVarRequestOptions, configuration);
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration);
localVarHeaderParameter["Content-Type"] = "application/json";
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions =
baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {
...localVarHeaderParameter,
...headersFromBaseOptions,
...options.headers,
};
localVarRequestOptions.data = serializeDataIfNeeded(
passwordRequest,
localVarRequestOptions,
configuration
);
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Disable Two-factor authentication
* @param {PasswordRequest} [passwordRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
disableTwoFactor: async (
passwordRequest?: PasswordRequest,
options: AxiosRequestConfig = {}
): Promise<RequestArgs> => {
const localVarPath = `/apis/api.security.halo.run/v1alpha1/authentications/two-factor/settings/disabled`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = {
method: "PUT",
...baseOptions,
...options,
};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication BasicAuth required
// http basic authentication required
setBasicAuthToObject(localVarRequestOptions, configuration);
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration);
localVarHeaderParameter["Content-Type"] = "application/json";
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions =
baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {
...localVarHeaderParameter,
...headersFromBaseOptions,
...options.headers,
};
localVarRequestOptions.data = serializeDataIfNeeded(
passwordRequest,
localVarRequestOptions,
configuration
);
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Enable Two-factor authentication
* @param {PasswordRequest} [passwordRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
enableTwoFactor: async (
passwordRequest?: PasswordRequest,
options: AxiosRequestConfig = {}
): Promise<RequestArgs> => {
const localVarPath = `/apis/api.security.halo.run/v1alpha1/authentications/two-factor/settings/enabled`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = {
method: "PUT",
...baseOptions,
...options,
};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication BasicAuth required
// http basic authentication required
setBasicAuthToObject(localVarRequestOptions, configuration);
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration);
localVarHeaderParameter["Content-Type"] = "application/json";
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions =
baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {
...localVarHeaderParameter,
...headersFromBaseOptions,
...options.headers,
};
localVarRequestOptions.data = serializeDataIfNeeded(
passwordRequest,
localVarRequestOptions,
configuration
);
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Get TOTP auth link, including secret
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getTotpAuthLink: async (
options: AxiosRequestConfig = {}
): Promise<RequestArgs> => {
const localVarPath = `/apis/api.security.halo.run/v1alpha1/authentications/two-factor/totp/auth-link`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = {
method: "GET",
...baseOptions,
...options,
};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication BasicAuth required
// http basic authentication required
setBasicAuthToObject(localVarRequestOptions, configuration);
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration);
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions =
baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {
...localVarHeaderParameter,
...headersFromBaseOptions,
...options.headers,
};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Get Two-factor authentication settings.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getTwoFactorAuthenticationSettings: async (
options: AxiosRequestConfig = {}
): Promise<RequestArgs> => {
const localVarPath = `/apis/api.security.halo.run/v1alpha1/authentications/two-factor/settings`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = {
method: "GET",
...baseOptions,
...options,
};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication BasicAuth required
// http basic authentication required
setBasicAuthToObject(localVarRequestOptions, configuration);
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration);
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions =
baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {
...localVarHeaderParameter,
...headersFromBaseOptions,
...options.headers,
};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
};
};
/**
* ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi - functional programming interface
* @export
*/
export const ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiFp = function (
configuration?: Configuration
) {
const localVarAxiosParamCreator =
ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiAxiosParamCreator(
configuration
);
return {
/**
* Configure a TOTP
* @param {TotpRequest} [totpRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async configurerTotp(
totpRequest?: TotpRequest,
options?: AxiosRequestConfig
): Promise<
(
axios?: AxiosInstance,
basePath?: string
) => AxiosPromise<TwoFactorAuthSettings>
> {
const localVarAxiosArgs = await localVarAxiosParamCreator.configurerTotp(
totpRequest,
options
);
return createRequestFunction(
localVarAxiosArgs,
globalAxios,
BASE_PATH,
configuration
);
},
/**
*
* @param {PasswordRequest} [passwordRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async deleteTotp(
passwordRequest?: PasswordRequest,
options?: AxiosRequestConfig
): Promise<
(
axios?: AxiosInstance,
basePath?: string
) => AxiosPromise<TwoFactorAuthSettings>
> {
const localVarAxiosArgs = await localVarAxiosParamCreator.deleteTotp(
passwordRequest,
options
);
return createRequestFunction(
localVarAxiosArgs,
globalAxios,
BASE_PATH,
configuration
);
},
/**
* Disable Two-factor authentication
* @param {PasswordRequest} [passwordRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async disableTwoFactor(
passwordRequest?: PasswordRequest,
options?: AxiosRequestConfig
): Promise<
(
axios?: AxiosInstance,
basePath?: string
) => AxiosPromise<TwoFactorAuthSettings>
> {
const localVarAxiosArgs =
await localVarAxiosParamCreator.disableTwoFactor(
passwordRequest,
options
);
return createRequestFunction(
localVarAxiosArgs,
globalAxios,
BASE_PATH,
configuration
);
},
/**
* Enable Two-factor authentication
* @param {PasswordRequest} [passwordRequest]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async enableTwoFactor(
passwordRequest?: PasswordRequest,
options?: AxiosRequestConfig
): Promise<
(
axios?: AxiosInstance,
basePath?: string
) => AxiosPromise<TwoFactorAuthSettings>
> {
const localVarAxiosArgs = await localVarAxiosParamCreator.enableTwoFactor(
passwordRequest,
options
);
return createRequestFunction(
localVarAxiosArgs,
globalAxios,
BASE_PATH,
configuration
);
},
/**
* Get TOTP auth link, including secret
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getTotpAuthLink(
options?: AxiosRequestConfig
): Promise<
(
axios?: AxiosInstance,
basePath?: string
) => AxiosPromise<TotpAuthLinkResponse>
> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getTotpAuthLink(
options
);
return createRequestFunction(
localVarAxiosArgs,
globalAxios,
BASE_PATH,
configuration
);
},
/**
* Get Two-factor authentication settings.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getTwoFactorAuthenticationSettings(
options?: AxiosRequestConfig
): Promise<
(
axios?: AxiosInstance,
basePath?: string
) => AxiosPromise<TwoFactorAuthSettings>
> {
const localVarAxiosArgs =
await localVarAxiosParamCreator.getTwoFactorAuthenticationSettings(
options
);
return createRequestFunction(
localVarAxiosArgs,
globalAxios,
BASE_PATH,
configuration
);
},
};
};
/**
* ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi - factory interface
* @export
*/
export const ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiFactory =
function (
configuration?: Configuration,
basePath?: string,
axios?: AxiosInstance
) {
const localVarFp =
ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiFp(configuration);
return {
/**
* Configure a TOTP
* @param {ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiConfigurerTotpRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
configurerTotp(
requestParameters: ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiConfigurerTotpRequest = {},
options?: AxiosRequestConfig
): AxiosPromise<TwoFactorAuthSettings> {
return localVarFp
.configurerTotp(requestParameters.totpRequest, options)
.then((request) => request(axios, basePath));
},
/**
*
* @param {ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDeleteTotpRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
deleteTotp(
requestParameters: ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDeleteTotpRequest = {},
options?: AxiosRequestConfig
): AxiosPromise<TwoFactorAuthSettings> {
return localVarFp
.deleteTotp(requestParameters.passwordRequest, options)
.then((request) => request(axios, basePath));
},
/**
* Disable Two-factor authentication
* @param {ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDisableTwoFactorRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
disableTwoFactor(
requestParameters: ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDisableTwoFactorRequest = {},
options?: AxiosRequestConfig
): AxiosPromise<TwoFactorAuthSettings> {
return localVarFp
.disableTwoFactor(requestParameters.passwordRequest, options)
.then((request) => request(axios, basePath));
},
/**
* Enable Two-factor authentication
* @param {ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiEnableTwoFactorRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
enableTwoFactor(
requestParameters: ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiEnableTwoFactorRequest = {},
options?: AxiosRequestConfig
): AxiosPromise<TwoFactorAuthSettings> {
return localVarFp
.enableTwoFactor(requestParameters.passwordRequest, options)
.then((request) => request(axios, basePath));
},
/**
* Get TOTP auth link, including secret
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getTotpAuthLink(
options?: AxiosRequestConfig
): AxiosPromise<TotpAuthLinkResponse> {
return localVarFp
.getTotpAuthLink(options)
.then((request) => request(axios, basePath));
},
/**
* Get Two-factor authentication settings.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getTwoFactorAuthenticationSettings(
options?: AxiosRequestConfig
): AxiosPromise<TwoFactorAuthSettings> {
return localVarFp
.getTwoFactorAuthenticationSettings(options)
.then((request) => request(axios, basePath));
},
};
};
/**
* Request parameters for configurerTotp operation in ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi.
* @export
* @interface ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiConfigurerTotpRequest
*/
export interface ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiConfigurerTotpRequest {
/**
*
* @type {TotpRequest}
* @memberof ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiConfigurerTotp
*/
readonly totpRequest?: TotpRequest;
}
/**
* Request parameters for deleteTotp operation in ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi.
* @export
* @interface ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDeleteTotpRequest
*/
export interface ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDeleteTotpRequest {
/**
*
* @type {PasswordRequest}
* @memberof ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDeleteTotp
*/
readonly passwordRequest?: PasswordRequest;
}
/**
* Request parameters for disableTwoFactor operation in ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi.
* @export
* @interface ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDisableTwoFactorRequest
*/
export interface ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDisableTwoFactorRequest {
/**
*
* @type {PasswordRequest}
* @memberof ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDisableTwoFactor
*/
readonly passwordRequest?: PasswordRequest;
}
/**
* Request parameters for enableTwoFactor operation in ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi.
* @export
* @interface ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiEnableTwoFactorRequest
*/
export interface ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiEnableTwoFactorRequest {
/**
*
* @type {PasswordRequest}
* @memberof ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiEnableTwoFactor
*/
readonly passwordRequest?: PasswordRequest;
}
/**
* ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi - object-oriented interface
* @export
* @class ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi
* @extends {BaseAPI}
*/
export class ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi extends BaseAPI {
/**
* Configure a TOTP
* @param {ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiConfigurerTotpRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi
*/
public configurerTotp(
requestParameters: ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiConfigurerTotpRequest = {},
options?: AxiosRequestConfig
) {
return ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiFp(
this.configuration
)
.configurerTotp(requestParameters.totpRequest, options)
.then((request) => request(this.axios, this.basePath));
}
/**
*
* @param {ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDeleteTotpRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi
*/
public deleteTotp(
requestParameters: ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDeleteTotpRequest = {},
options?: AxiosRequestConfig
) {
return ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiFp(
this.configuration
)
.deleteTotp(requestParameters.passwordRequest, options)
.then((request) => request(this.axios, this.basePath));
}
/**
* Disable Two-factor authentication
* @param {ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDisableTwoFactorRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi
*/
public disableTwoFactor(
requestParameters: ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiDisableTwoFactorRequest = {},
options?: AxiosRequestConfig
) {
return ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiFp(
this.configuration
)
.disableTwoFactor(requestParameters.passwordRequest, options)
.then((request) => request(this.axios, this.basePath));
}
/**
* Enable Two-factor authentication
* @param {ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiEnableTwoFactorRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi
*/
public enableTwoFactor(
requestParameters: ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiEnableTwoFactorRequest = {},
options?: AxiosRequestConfig
) {
return ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiFp(
this.configuration
)
.enableTwoFactor(requestParameters.passwordRequest, options)
.then((request) => request(this.axios, this.basePath));
}
/**
* Get TOTP auth link, including secret
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi
*/
public getTotpAuthLink(options?: AxiosRequestConfig) {
return ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiFp(
this.configuration
)
.getTotpAuthLink(options)
.then((request) => request(this.axios, this.basePath));
}
/**
* Get Two-factor authentication settings.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi
*/
public getTwoFactorAuthenticationSettings(options?: AxiosRequestConfig) {
return ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApiFp(
this.configuration
)
.getTwoFactorAuthenticationSettings(options)
.then((request) => request(this.axios, this.basePath));
}
}

View File

@ -106,6 +106,7 @@ export * from "./notifier-descriptor-spec";
export * from "./notifier-info";
export * from "./notifier-setting-ref";
export * from "./owner-info";
export * from "./password-request";
export * from "./password-reset-email-request";
export * from "./pat-spec";
export * from "./personal-access-token";
@ -203,6 +204,9 @@ export * from "./theme";
export * from "./theme-list";
export * from "./theme-spec";
export * from "./theme-status";
export * from "./totp-auth-link-response";
export * from "./totp-request";
export * from "./two-factor-auth-settings";
export * from "./upgrade-from-uri-request";
export * from "./user";
export * from "./user-connection";

View File

@ -0,0 +1,27 @@
/* tslint:disable */
/* eslint-disable */
/**
* Halo Next API
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 2.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
*
* @export
* @interface PasswordRequest
*/
export interface PasswordRequest {
/**
*
* @type {string}
* @memberof PasswordRequest
*/
password: string;
}

View File

@ -0,0 +1,33 @@
/* tslint:disable */
/* eslint-disable */
/**
* Halo Next API
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 2.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
*
* @export
* @interface TotpAuthLinkResponse
*/
export interface TotpAuthLinkResponse {
/**
*
* @type {string}
* @memberof TotpAuthLinkResponse
*/
authLink?: string;
/**
*
* @type {string}
* @memberof TotpAuthLinkResponse
*/
rawSecret?: string;
}

View File

@ -0,0 +1,39 @@
/* tslint:disable */
/* eslint-disable */
/**
* Halo Next API
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 2.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
*
* @export
* @interface TotpRequest
*/
export interface TotpRequest {
/**
*
* @type {string}
* @memberof TotpRequest
*/
code: string;
/**
*
* @type {string}
* @memberof TotpRequest
*/
password: string;
/**
*
* @type {string}
* @memberof TotpRequest
*/
secret: string;
}

View File

@ -0,0 +1,45 @@
/* tslint:disable */
/* eslint-disable */
/**
* Halo Next API
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 2.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
*
* @export
* @interface TwoFactorAuthSettings
*/
export interface TwoFactorAuthSettings {
/**
*
* @type {boolean}
* @memberof TwoFactorAuthSettings
*/
available?: boolean;
/**
*
* @type {boolean}
* @memberof TwoFactorAuthSettings
*/
emailVerified?: boolean;
/**
*
* @type {boolean}
* @memberof TwoFactorAuthSettings
*/
enabled?: boolean;
/**
*
* @type {boolean}
* @memberof TwoFactorAuthSettings
*/
totpConfigured?: boolean;
}

View File

@ -78,6 +78,12 @@ export interface UserSpec {
* @memberof UserSpec
*/
registeredAt?: string;
/**
*
* @type {string}
* @memberof UserSpec
*/
totpEncryptedSecret?: string;
/**
*
* @type {boolean}

View File

@ -110,6 +110,9 @@ importers:
'@vueuse/core':
specifier: ^10.3.0
version: 10.3.0(vue@3.3.4)
'@vueuse/integrations':
specifier: ^10.5.0
version: 10.7.1(axios@0.27.2)(fuse.js@6.6.2)(qrcode@1.5.3)(vue@3.3.4)
'@vueuse/router':
specifier: ^10.3.0
version: 10.3.0(vue-router@4.2.4)(vue@3.3.4)
@ -164,6 +167,9 @@ importers:
pretty-bytes:
specifier: ^6.0.0
version: 6.0.0
qrcode:
specifier: ^1.5.3
version: 1.5.3
qs:
specifier: ^6.11.1
version: 6.11.1
@ -6519,6 +6525,10 @@ packages:
resolution: {integrity: sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA==}
dev: false
/@types/web-bluetooth@0.0.20:
resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
dev: false
/@types/yargs-parser@21.0.3:
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
dev: true
@ -7199,10 +7209,78 @@ packages:
- vue
dev: false
/@vueuse/core@10.7.1(vue@3.3.4):
resolution: {integrity: sha512-74mWHlaesJSWGp1ihg76vAnfVq9NTv1YT0SYhAQ6zwFNdBkkP+CKKJmVOEHcdSnLXCXYiL5e7MaewblfiYLP7g==}
dependencies:
'@types/web-bluetooth': 0.0.20
'@vueuse/metadata': 10.7.1
'@vueuse/shared': 10.7.1(vue@3.3.4)
vue-demi: 0.14.6(vue@3.3.4)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
dev: false
/@vueuse/integrations@10.7.1(axios@0.27.2)(fuse.js@6.6.2)(qrcode@1.5.3)(vue@3.3.4):
resolution: {integrity: sha512-cKo5LEeKVHdBRBtMTOrDPdR0YNtrmN9IBfdcnY2P3m5LHVrsD0xiHUtAH1WKjHQRIErZG6rJUa6GA4tWZt89Og==}
peerDependencies:
async-validator: '*'
axios: '*'
change-case: '*'
drauu: '*'
focus-trap: '*'
fuse.js: '*'
idb-keyval: '*'
jwt-decode: '*'
nprogress: '*'
qrcode: '*'
sortablejs: '*'
universal-cookie: '*'
peerDependenciesMeta:
async-validator:
optional: true
axios:
optional: true
change-case:
optional: true
drauu:
optional: true
focus-trap:
optional: true
fuse.js:
optional: true
idb-keyval:
optional: true
jwt-decode:
optional: true
nprogress:
optional: true
qrcode:
optional: true
sortablejs:
optional: true
universal-cookie:
optional: true
dependencies:
'@vueuse/core': 10.7.1(vue@3.3.4)
'@vueuse/shared': 10.7.1(vue@3.3.4)
axios: 0.27.2
fuse.js: 6.6.2
qrcode: 1.5.3
vue-demi: 0.14.6(vue@3.3.4)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
dev: false
/@vueuse/metadata@10.3.0:
resolution: {integrity: sha512-Ema3YhNOa4swDsV0V7CEY5JXvK19JI/o1szFO1iWxdFg3vhdFtCtSTP26PCvbUpnUtNHBY2wx5y3WDXND5Pvnw==}
dev: false
/@vueuse/metadata@10.7.1:
resolution: {integrity: sha512-jX8MbX5UX067DYVsbtrmKn6eG6KMcXxLRLlurGkZku5ZYT3vxgBjui2zajvUZ18QLIjrgBkFRsu7CqTAg18QFw==}
dev: false
/@vueuse/router@10.3.0(vue-router@4.2.4)(vue@3.3.4):
resolution: {integrity: sha512-WCx/BAxO0eInuOcyNRBxDLS16tnNqzdaR6/babg6AUgAIL0TCfmHBh46wJa6hhg+NMGjd6HzCaktxBasp+0c0A==}
peerDependencies:
@ -7225,6 +7303,15 @@ packages:
- vue
dev: false
/@vueuse/shared@10.7.1(vue@3.3.4):
resolution: {integrity: sha512-v0jbRR31LSgRY/C5i5X279A/WQjD6/JsMzGa+eqt658oJ75IvQXAeONmwvEMrvJQKnRElq/frzBR7fhmWY5uLw==}
dependencies:
vue-demi: 0.14.6(vue@3.3.4)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
dev: false
/@webassemblyjs/ast@1.11.6:
resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==}
dependencies:
@ -8181,7 +8268,6 @@ packages:
/camelcase@5.3.1:
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
engines: {node: '>=6'}
dev: true
/camelcase@6.3.0:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
@ -8400,7 +8486,6 @@ packages:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 6.2.0
dev: true
/cliui@7.0.4:
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
@ -8978,7 +9063,6 @@ packages:
/decamelize@1.2.0:
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
engines: {node: '>=0.10.0'}
dev: true
/decimal.js@10.4.2:
resolution: {integrity: sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==}
@ -9192,6 +9276,10 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
/dijkstrajs@1.0.3:
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
dev: false
/dir-glob@3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
@ -9367,6 +9455,10 @@ packages:
engines: {node: '>= 4'}
dev: true
/encode-utf8@1.0.3:
resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
dev: false
/encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
@ -10608,7 +10700,6 @@ packages:
dependencies:
locate-path: 5.0.0
path-exists: 4.0.0
dev: true
/find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
@ -12579,7 +12670,6 @@ packages:
engines: {node: '>=8'}
dependencies:
p-locate: 4.1.0
dev: true
/locate-path@6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
@ -13581,7 +13671,6 @@ packages:
engines: {node: '>=6'}
dependencies:
p-try: 2.2.0
dev: true
/p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
@ -13609,7 +13698,6 @@ packages:
engines: {node: '>=8'}
dependencies:
p-limit: 2.3.0
dev: true
/p-locate@5.0.0:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
@ -13655,7 +13743,6 @@ packages:
/p-try@2.2.0:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
dev: true
/pac-proxy-agent@7.0.1:
resolution: {integrity: sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==}
@ -13773,7 +13860,6 @@ packages:
/path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
dev: true
/path-is-absolute@1.0.1:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
@ -13958,6 +14044,11 @@ packages:
pathe: 1.1.1
dev: true
/pngjs@5.0.0:
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
engines: {node: '>=10.13.0'}
dev: false
/polished@4.2.2:
resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==}
engines: {node: '>=10'}
@ -14583,6 +14674,17 @@ packages:
- utf-8-validate
dev: true
/qrcode@1.5.3:
resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==}
engines: {node: '>=10.13.0'}
hasBin: true
dependencies:
dijkstrajs: 1.0.3
encode-utf8: 1.0.3
pngjs: 5.0.0
yargs: 15.4.1
dev: false
/qs@6.11.0:
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
engines: {node: '>=0.6'}
@ -14996,7 +15098,6 @@ packages:
/require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
dev: true
/requireindex@1.2.0:
resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==}
@ -15427,7 +15528,6 @@ packages:
/set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
dev: true
/set-function-length@1.1.1:
resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==}
@ -17288,6 +17388,21 @@ packages:
vue: 3.3.4
dev: false
/vue-demi@0.14.6(vue@3.3.4):
resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: ^3.0.0-0 || ^2.6.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
dependencies:
vue: 3.3.4
dev: false
/vue-docgen-api@4.75.1(vue@3.3.4):
resolution: {integrity: sha512-MECZ3uExz+ssmhD/2XrFoQQs93y17IVO1KDYTp8nr6i9GNrk67AAto6QAtilW1H/pTDPMkQxJ7w/25ZIqVtfAA==}
peerDependencies:
@ -17612,7 +17727,6 @@ packages:
/which-module@2.0.0:
resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==}
dev: true
/which-pm@2.0.0:
resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==}
@ -17851,7 +17965,6 @@ packages:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
dev: true
/wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
@ -17946,7 +18059,6 @@ packages:
/y18n@4.0.3:
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
dev: true
/y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
@ -17988,7 +18100,6 @@ packages:
dependencies:
camelcase: 5.3.1
decamelize: 1.2.0
dev: true
/yargs-parser@20.2.9:
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
@ -18014,7 +18125,6 @@ packages:
which-module: 2.0.0
y18n: 4.0.3
yargs-parser: 18.1.3
dev: true
/yargs@16.2.0:
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}

View File

@ -11,6 +11,8 @@ import { submitForm } from "@formkit/core";
import { JSEncrypt } from "jsencrypt";
import { apiClient } from "@/utils/api-client";
import { useI18n } from "vue-i18n";
import { ERROR_MFA_REQUIRED_TYPE } from "@/constants/error-types";
import MfaForm from "./MfaForm.vue";
const { t } = useI18n();
@ -68,6 +70,7 @@ const handleLogin = async () => {
withCredentials: true,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"X-Requested-With": "XMLHttpRequest",
},
}
);
@ -92,7 +95,16 @@ const handleLogin = async () => {
return;
}
const { title: errorTitle, detail: errorDetail } = e.response?.data || {};
const {
title: errorTitle,
detail: errorDetail,
type: errorType,
} = e.response?.data || {};
if (errorType === ERROR_MFA_REQUIRED_TYPE) {
mfaRequired.value = true;
return;
}
if (errorTitle || errorDetail) {
Toast.error(errorDetail || errorTitle);
@ -117,9 +129,13 @@ onMounted(() => {
const inputClasses = {
outer: "!py-3 first:!pt-0 last:!pb-0",
};
// mfa
const mfaRequired = ref(false);
</script>
<template>
<template v-if="!mfaRequired">
<FormKit
id="login-form"
v-model="loginForm"
@ -164,4 +180,6 @@ const inputClasses = {
>
{{ $t(buttonText) }}
</VButton>
</template>
<MfaForm v-else @succeed="$emit('succeed')" />
</template>

View File

@ -0,0 +1,72 @@
<script lang="ts" setup>
import { submitForm } from "@formkit/core";
import { Toast, VButton } from "@halo-dev/components";
import qs from "qs";
import axios from "axios";
const emit = defineEmits<{
(event: "succeed"): void;
}>();
async function onSubmit({ code }: { code: string }) {
try {
const _csrf = document.cookie
.split("; ")
.find((row) => row.startsWith("XSRF-TOKEN"))
?.split("=")[1];
if (!_csrf) {
Toast.warning("CSRF token not found");
return;
}
await axios.post(
`${import.meta.env.VITE_API_URL}/login/2fa/totp`,
qs.stringify({
code,
_csrf,
}),
{
withCredentials: true,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"X-Requested-With": "XMLHttpRequest",
},
}
);
emit("succeed");
} catch (error) {
Toast.error("验证失败");
}
}
</script>
<template>
<FormKit
id="mfa-form"
name="mfa-form"
type="form"
:classes="{
form: '!divide-none',
}"
:config="{ validationVisibility: 'submit' }"
@submit="onSubmit"
@keyup.enter="submitForm('mfa-form')"
>
<FormKit
:classes="{
outer: '!py-0',
}"
name="code"
placeholder="请输入两步验证码"
validation-label="两步验证码"
:autofocus="true"
type="text"
validation="required"
>
</FormKit>
</FormKit>
<VButton class="mt-8" block type="secondary" @click="submitForm('mfa-form')">
验证
</VButton>
</template>

View File

@ -0,0 +1 @@
export const ERROR_MFA_REQUIRED_TYPE = "https://halo.run/probs/2fa-required";

View File

@ -45,6 +45,7 @@ import {
NotificationHaloRunV1alpha1NotifierDescriptorApi,
ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi,
SecurityHaloRunV1alpha1PersonalAccessTokenApi,
ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi,
UcApiContentHaloRunV1alpha1AttachmentApi,
UcApiContentHaloRunV1alpha1PostApi,
UcApiContentHaloRunV1alpha1SnapshotApi,
@ -247,6 +248,11 @@ function setupApiClient(axios: AxiosInstance) {
baseURL,
axios
),
twoFactor: new ApiSecurityHaloRunV1alpha1AuthenticationTwoFactorApi(
undefined,
baseURL,
axios
),
uc: {
post: new UcApiContentHaloRunV1alpha1PostApi(undefined, baseURL, axios),
attachment: new UcApiContentHaloRunV1alpha1AttachmentApi(

View File

@ -22,6 +22,7 @@ import DetailTab from "./tabs/Detail.vue";
import PersonalAccessTokensTab from "./tabs/PersonalAccessTokens.vue";
import { useRouteQuery } from "@vueuse/router";
import NotificationPreferences from "./tabs/NotificationPreferences.vue";
import TwoFactor from "./tabs/TwoFactor.vue";
const { t } = useI18n();
@ -79,6 +80,12 @@ const tabs: UserTab[] = [
component: markRaw(PersonalAccessTokensTab),
priority: 30,
},
{
id: "2fa",
label: "两步验证",
component: markRaw(TwoFactor),
priority: 40,
},
];
const tabbarItems = computed(() => {

View File

@ -0,0 +1,128 @@
<script lang="ts" setup>
import TotpConfigureModal from "./components/TotpConfigureModal.vue";
import TotpDeletionModal from "./components/TotpDeletionModal.vue";
import { ref } from "vue";
import {
VButton,
VEntity,
VEntityField,
VLoading,
VSpace,
} from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query";
import { apiClient } from "@/utils/api-client";
import StatusDotField from "@/components/entity-fields/StatusDotField.vue";
import RiShieldKeyholeLine from "~icons/ri/shield-keyhole-line";
import TwoFactorEnableModal from "./components/TwoFactorEnableModal.vue";
import TwoFactorDisableModal from "./components/TwoFactorDisableModal.vue";
const { data: settings, isLoading } = useQuery({
queryKey: ["two-factor-settings"],
queryFn: async () => {
const { data } =
await apiClient.twoFactor.getTwoFactorAuthenticationSettings();
return data;
},
});
const twoFactorEnableModalVisible = ref(false);
const twoFactorDisableModalVisible = ref(false);
function onEnabledChange(payload: Event) {
const target = payload.target as HTMLInputElement;
// Do not change the checked state of the checkbox
target.checked = !target.checked;
if (settings.value?.enabled) {
twoFactorDisableModalVisible.value = true;
} else {
twoFactorEnableModalVisible.value = true;
}
}
const totpConfigureModalVisible = ref(false);
const totpDeletionModalVisible = ref(false);
</script>
<template>
<div class="my-5">
<label class="flex cursor-pointer items-center space-x-2">
<input
type="checkbox"
:checked="settings?.enabled"
@change="onEnabledChange"
/>
<span class="text-sm font-medium text-gray-700">启用两步验证</span>
</label>
</div>
<VLoading v-if="isLoading" />
<Transition v-else appear name="fade">
<ul
class="box-border h-full w-full divide-y divide-gray-100 overflow-hidden rounded-base border"
role="list"
>
<li class="bg-gray-50 px-4 py-3">
<span class="text-sm font-semibold text-gray-900">验证方式</span>
</li>
<li>
<VEntity>
<template #start>
<VEntityField>
<template #description>
<RiShieldKeyholeLine />
</template>
</VEntityField>
<VEntityField
title="TOTP"
description="使用 TOTP 应用程序配置两步验证"
/>
</template>
<template #end>
<StatusDotField
:state="settings?.totpConfigured ? 'success' : 'default'"
:text="settings?.totpConfigured ? '已配置' : '未配置'"
></StatusDotField>
<VEntityField>
<template #description>
<VSpace>
<VButton size="sm" @click="totpConfigureModalVisible = true">
{{ settings?.totpConfigured ? "重新配置" : "配置" }}
</VButton>
<VButton
v-if="settings?.totpConfigured"
size="sm"
type="danger"
@click="totpDeletionModalVisible = true"
>
停用
</VButton>
</VSpace>
</template>
</VEntityField>
</template>
</VEntity>
</li>
</ul>
</Transition>
<TotpConfigureModal
v-if="totpConfigureModalVisible"
@close="totpConfigureModalVisible = false"
/>
<TotpDeletionModal
v-if="totpDeletionModalVisible"
@close="totpDeletionModalVisible = false"
/>
<TwoFactorEnableModal
v-if="twoFactorEnableModalVisible"
@close="twoFactorEnableModalVisible = false"
/>
<TwoFactorDisableModal
v-if="twoFactorDisableModalVisible"
@close="twoFactorDisableModalVisible = false"
/>
</template>

View File

@ -0,0 +1,27 @@
<script lang="ts" setup>
const emit = defineEmits<{
(event: "submit", password: string): void;
}>();
function onSubmit({ password }: { password: string }) {
emit("submit", password);
}
</script>
<template>
<FormKit
id="password-validation-form"
type="form"
name="password-validation-form"
@submit="onSubmit"
>
<FormKit
type="password"
label="验证密码"
validation="required"
name="password"
help="当前账号的登录密码"
autocomplete="current-password"
></FormKit>
</FormKit>
</template>

View File

@ -0,0 +1,113 @@
<script lang="ts" setup>
import { apiClient } from "@/utils/api-client";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import { useQRCode } from "@vueuse/integrations/useQRCode";
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import { computed } from "vue";
import type { TotpRequest } from "@halo-dev/api-client";
import { ref } from "vue";
const queryClient = useQueryClient();
const emit = defineEmits<{
(event: "close"): void;
}>();
const modal = ref();
const { data } = useQuery({
queryKey: ["totp-auth-link"],
queryFn: async () => {
const { data } = await apiClient.twoFactor.getTotpAuthLink();
return data;
},
});
const qrcode = useQRCode(computed(() => data.value?.authLink || ""));
const { mutate, isLoading } = useMutation({
mutationKey: ["configure-totp"],
mutationFn: async ({ totpRequest }: { totpRequest: TotpRequest }) => {
await apiClient.twoFactor.configurerTotp({
totpRequest: totpRequest,
});
},
onSuccess() {
Toast.success("配置成功");
queryClient.invalidateQueries({ queryKey: ["two-factor-settings"] });
modal.value.close();
},
});
function onSubmit(data: TotpRequest) {
mutate({ totpRequest: data });
}
</script>
<template>
<VModal
ref="modal"
:width="500"
:centered="false"
title="TOTP 配置"
@close="emit('close')"
>
<div>
<div class="mb-4 space-y-3 border-b border-gray-100 pb-4 text-gray-900">
<div class="text-sm font-semibold">使用验证器应用扫描下方二维码</div>
<img :src="qrcode" class="rounded-base border border-gray-100" />
<details>
<summary class="cursor-pointer select-none text-sm text-gray-800">
如果无法扫描二维码点击查看代替步骤
</summary>
<div class="mt-3 rounded-base border border-gray-100 p-2">
<span class="text-sm text-gray-600">
使用以下代码手动配置验证器应用
</span>
<div class="mt-2">
<code
class="select-all rounded bg-gray-200 p-1 text-xs text-gray-900"
>
{{ data?.rawSecret }}
</code>
</div>
</div>
</details>
</div>
<FormKit id="totp-form" type="form" name="totp-form" @submit="onSubmit">
<FormKit
type="number"
name="code"
label="验证码"
validation="required"
help="从验证器应用获得的 6 位验证码"
></FormKit>
<FormKit
type="password"
label="验证密码"
validation="required"
name="password"
help="当前账号的登录密码"
autocomplete="current-password"
></FormKit>
<FormKit
:model-value="data?.rawSecret"
type="hidden"
name="secret"
></FormKit>
</FormKit>
</div>
<template #footer>
<VSpace>
<VButton
:loading="isLoading"
type="secondary"
@click="$formkit.submit('totp-form')"
>
完成
</VButton>
<VButton @click="modal.close()"></VButton>
</VSpace>
</template>
</VModal>
</template>

View File

@ -0,0 +1,59 @@
<script lang="ts" setup>
import { apiClient } from "@/utils/api-client";
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { ref } from "vue";
import PasswordValidationForm from "./PasswordValidationForm.vue";
const queryClient = useQueryClient();
const emit = defineEmits<{
(event: "close"): void;
}>();
const modal = ref();
const { mutate, isLoading } = useMutation({
mutationKey: ["totp-deletion"],
mutationFn: async ({ password }: { password: string }) => {
return await apiClient.twoFactor.deleteTotp({
passwordRequest: {
password: password,
},
});
},
onSuccess() {
Toast.success("停用成功");
queryClient.invalidateQueries({ queryKey: ["two-factor-settings"] });
modal.value.close();
},
});
function onSubmit(password: string) {
mutate({ password });
}
</script>
<template>
<VModal
ref="modal"
:width="500"
:centered="false"
title="停用 TOTP"
@close="emit('close')"
>
<PasswordValidationForm @submit="onSubmit" />
<template #footer>
<VSpace>
<VButton
:loading="isLoading"
type="danger"
@click="$formkit.submit('password-validation-form')"
>
停用
</VButton>
<VButton @click="modal.close()"></VButton>
</VSpace>
</template>
</VModal>
</template>

View File

@ -0,0 +1,59 @@
<script lang="ts" setup>
import { apiClient } from "@/utils/api-client";
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { ref } from "vue";
import PasswordValidationForm from "./PasswordValidationForm.vue";
const queryClient = useQueryClient();
const emit = defineEmits<{
(event: "close"): void;
}>();
const modal = ref();
const { mutate, isLoading } = useMutation({
mutationKey: ["disable-two-factor"],
mutationFn: async ({ password }: { password: string }) => {
return await apiClient.twoFactor.disableTwoFactor({
passwordRequest: {
password: password,
},
});
},
onSuccess() {
Toast.success("停用成功");
queryClient.invalidateQueries({ queryKey: ["two-factor-settings"] });
modal.value.close();
},
});
function onSubmit(password: string) {
mutate({ password });
}
</script>
<template>
<VModal
ref="modal"
:width="500"
:centered="false"
title="停用两步验证"
@close="emit('close')"
>
<PasswordValidationForm @submit="onSubmit" />
<template #footer>
<VSpace>
<VButton
:loading="isLoading"
type="danger"
@click="$formkit.submit('password-validation-form')"
>
停用
</VButton>
<VButton @click="modal.close()"></VButton>
</VSpace>
</template>
</VModal>
</template>

View File

@ -0,0 +1,59 @@
<script lang="ts" setup>
import { apiClient } from "@/utils/api-client";
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { ref } from "vue";
import PasswordValidationForm from "./PasswordValidationForm.vue";
const queryClient = useQueryClient();
const emit = defineEmits<{
(event: "close"): void;
}>();
const modal = ref();
const { mutate, isLoading } = useMutation({
mutationKey: ["enable-two-factor"],
mutationFn: async ({ password }: { password: string }) => {
return await apiClient.twoFactor.enableTwoFactor({
passwordRequest: {
password: password,
},
});
},
onSuccess() {
Toast.success("启用成功");
queryClient.invalidateQueries({ queryKey: ["two-factor-settings"] });
modal.value.close();
},
});
function onSubmit(password: string) {
mutate({ password });
}
</script>
<template>
<VModal
ref="modal"
:width="500"
:centered="false"
title="启用两步验证"
@close="emit('close')"
>
<PasswordValidationForm @submit="onSubmit" />
<template #footer>
<VSpace>
<VButton
:loading="isLoading"
type="secondary"
@click="$formkit.submit('password-validation-form')"
>
启用
</VButton>
<VButton @click="modal.close()"></VButton>
</VSpace>
</template>
</VModal>
</template>