diff --git a/packages/auth0-acul-js/interfaces/models/transaction.ts b/packages/auth0-acul-js/interfaces/models/transaction.ts index d74d41c8e..f0313666b 100644 --- a/packages/auth0-acul-js/interfaces/models/transaction.ts +++ b/packages/auth0-acul-js/interfaces/models/transaction.ts @@ -104,6 +104,9 @@ export interface DBConnection extends Connection { enabled: boolean; }; }; + password_options?: { + complexity: any // TODO, this should be added to auth0-server https://github.com/atko-cic/auth0-server/blob/b957b6eaac93b69bc72a836e4774c6184a5e2a92/packages/%40types/types/packages/Acul.ts#L181 + } }; } diff --git a/packages/auth0-acul-js/interfaces/screens/signup.ts b/packages/auth0-acul-js/interfaces/screens/signup.ts index dbfe03372..9c8f97e8a 100644 --- a/packages/auth0-acul-js/interfaces/screens/signup.ts +++ b/packages/auth0-acul-js/interfaces/screens/signup.ts @@ -1,7 +1,11 @@ -import type { IdentifierType } from '../../src/constants'; -import type { BaseMembers } from '../models/base-context'; -import type { ScreenMembers } from '../models/screen'; -import type { TransactionMembers, UsernamePolicy, PasswordPolicy } from '../models/transaction'; +import type { IdentifierType } from "../../src/constants"; +import type { BaseMembers } from "../models/base-context"; +import type { ScreenMembers } from "../models/screen"; +import type { + TransactionMembers, + UsernamePolicy, + PasswordPolicy, +} from "../models/transaction"; export interface SignupOptions { email?: string; @@ -27,6 +31,7 @@ export interface TransactionMembersOnSignup extends TransactionMembers { requiredIdentifiers: IdentifierType[] | null; optionalIdentifiers: IdentifierType[] | null; passwordPolicy: PasswordPolicy | null; + passwordComplexity?: any; // TODO: Define the type for password complexity (get from server https://github.com/atko-cic/auth0-server/blob/b957b6eaac93b69bc72a836e4774c6184a5e2a92/packages/%40types/types/packages/Acul.ts#L181) } export interface SignupMembers extends BaseMembers { diff --git a/packages/auth0-acul-js/src/screens/login/transaction-override.ts b/packages/auth0-acul-js/src/screens/login/transaction-override.ts index 1041ce484..8de15fa09 100644 --- a/packages/auth0-acul-js/src/screens/login/transaction-override.ts +++ b/packages/auth0-acul-js/src/screens/login/transaction-override.ts @@ -1,19 +1,28 @@ -import { ConnectionStrategy, Identifiers } from '../../../src/constants'; -import { Transaction } from '../../models/transaction'; -import { isSignupEnabled, isForgotPasswordEnabled, isPasskeyEnabled, getPasswordPolicy, getAllowedIdentifiers } from '../../shared/transaction'; +import { ConnectionStrategy, Identifiers } from "../../../src/constants"; +import { Transaction } from "../../models/transaction"; +import { + isSignupEnabled, + isForgotPasswordEnabled, + isPasskeyEnabled, + getPasswordPolicy, + getAllowedIdentifiers, +} from "../../shared/transaction"; -import type { TransactionContext } from '../../../interfaces/models/transaction'; -import type { TransactionMembersOnLogin as OverrideOptions } from '../../../interfaces/screens/login'; +import type { TransactionContext } from "../../../interfaces/models/transaction"; +import type { TransactionMembersOnLogin as OverrideOptions } from "../../../interfaces/screens/login"; /** * Login transaction override implementation */ -export class TransactionOverride extends Transaction implements OverrideOptions { - isSignupEnabled: OverrideOptions['isSignupEnabled']; - isForgotPasswordEnabled: OverrideOptions['isForgotPasswordEnabled']; - isPasskeyEnabled: OverrideOptions['isPasskeyEnabled']; - passwordPolicy: OverrideOptions['passwordPolicy']; - allowedIdentifiers: OverrideOptions['allowedIdentifiers']; +export class TransactionOverride + extends Transaction + implements OverrideOptions +{ + isSignupEnabled: OverrideOptions["isSignupEnabled"]; + isForgotPasswordEnabled: OverrideOptions["isForgotPasswordEnabled"]; + isPasskeyEnabled: OverrideOptions["isPasskeyEnabled"]; + passwordPolicy: OverrideOptions["passwordPolicy"]; + allowedIdentifiers: OverrideOptions["allowedIdentifiers"]; constructor(transactionContext: TransactionContext) { super(transactionContext); @@ -21,12 +30,20 @@ export class TransactionOverride extends Transaction implements OverrideOptions this.isForgotPasswordEnabled = isForgotPasswordEnabled(transactionContext); this.isPasskeyEnabled = isPasskeyEnabled(transactionContext); this.passwordPolicy = getPasswordPolicy(transactionContext); - this.allowedIdentifiers = TransactionOverride.getAllowedIdentifiers(transactionContext, this.connectionStrategy); + this.allowedIdentifiers = TransactionOverride.getAllowedIdentifiers( + transactionContext, + this.connectionStrategy + ); } - static getAllowedIdentifiers(transactionContext: TransactionContext, connectionStrategy: string | null): OverrideOptions['allowedIdentifiers'] { - if (connectionStrategy === ConnectionStrategy.SMS) return [Identifiers.PHONE]; - if (connectionStrategy === ConnectionStrategy.EMAIL) return [Identifiers.EMAIL]; + static getAllowedIdentifiers( + transactionContext: TransactionContext, + connectionStrategy: string | null + ): OverrideOptions["allowedIdentifiers"] { + if (connectionStrategy === ConnectionStrategy.SMS) + return [Identifiers.PHONE]; + if (connectionStrategy === ConnectionStrategy.EMAIL) + return [Identifiers.EMAIL]; return getAllowedIdentifiers(transactionContext); } } diff --git a/packages/auth0-acul-js/src/screens/signup/transaction-override.ts b/packages/auth0-acul-js/src/screens/signup/transaction-override.ts index 70ceffd1c..42cd74d0b 100644 --- a/packages/auth0-acul-js/src/screens/signup/transaction-override.ts +++ b/packages/auth0-acul-js/src/screens/signup/transaction-override.ts @@ -1,29 +1,49 @@ -import { ConnectionStrategy, Identifiers } from '../../../src/constants'; -import { Transaction } from '../../models/transaction'; -import { isPasskeyEnabled, getUsernamePolicy, getRequiredIdentifiers, getOptionalIdentifiers, getPasswordPolicy } from '../../shared/transaction'; +import { ConnectionStrategy, Identifiers } from "../../../src/constants"; +import { Transaction } from "../../models/transaction"; +import { + isPasskeyEnabled, + getUsernamePolicy, + getRequiredIdentifiers, + getOptionalIdentifiers, + getPasswordPolicy, + getPasswordComplexity, +} from "../../shared/transaction"; -import type { TransactionContext } from '../../../interfaces/models/transaction'; -import type { TransactionMembersOnSignup as OverrideOptions } from '../../../interfaces/screens/signup'; +import type { TransactionContext } from "../../../interfaces/models/transaction"; +import type { TransactionMembersOnSignup as OverrideOptions } from "../../../interfaces/screens/signup"; -export class TransactionOverride extends Transaction implements OverrideOptions { - isPasskeyEnabled: OverrideOptions['isPasskeyEnabled']; - usernamePolicy: OverrideOptions['usernamePolicy']; - optionalIdentifiers: OverrideOptions['optionalIdentifiers']; - requiredIdentifiers: OverrideOptions['requiredIdentifiers']; - passwordPolicy: OverrideOptions['passwordPolicy']; +export class TransactionOverride + extends Transaction + implements OverrideOptions +{ + isPasskeyEnabled: OverrideOptions["isPasskeyEnabled"]; + usernamePolicy: OverrideOptions["usernamePolicy"]; + optionalIdentifiers: OverrideOptions["optionalIdentifiers"]; + requiredIdentifiers: OverrideOptions["requiredIdentifiers"]; + passwordPolicy: OverrideOptions["passwordPolicy"]; + passwordComplexity: OverrideOptions["passwordComplexity"]; constructor(transactionContext: TransactionContext) { super(transactionContext); this.isPasskeyEnabled = isPasskeyEnabled(transactionContext); this.usernamePolicy = getUsernamePolicy(transactionContext); this.optionalIdentifiers = getOptionalIdentifiers(transactionContext); - this.requiredIdentifiers = TransactionOverride.getRequiredIdentifiers(transactionContext, this.connectionStrategy); + this.requiredIdentifiers = TransactionOverride.getRequiredIdentifiers( + transactionContext, + this.connectionStrategy + ); this.passwordPolicy = getPasswordPolicy(transactionContext); + this.passwordComplexity = getPasswordComplexity(transactionContext); } - static getRequiredIdentifiers(transactionContext: TransactionContext, connectionStrategy: string | null): OverrideOptions['requiredIdentifiers'] { - if (connectionStrategy === ConnectionStrategy.SMS) return [Identifiers.PHONE]; - if (connectionStrategy === ConnectionStrategy.EMAIL) return [Identifiers.EMAIL]; + static getRequiredIdentifiers( + transactionContext: TransactionContext, + connectionStrategy: string | null + ): OverrideOptions["requiredIdentifiers"] { + if (connectionStrategy === ConnectionStrategy.SMS) + return [Identifiers.PHONE]; + if (connectionStrategy === ConnectionStrategy.EMAIL) + return [Identifiers.EMAIL]; return getRequiredIdentifiers(transactionContext); } } diff --git a/packages/auth0-acul-js/src/shared/transaction.ts b/packages/auth0-acul-js/src/shared/transaction.ts index fcbc3327f..92bef7f31 100644 --- a/packages/auth0-acul-js/src/shared/transaction.ts +++ b/packages/auth0-acul-js/src/shared/transaction.ts @@ -1,7 +1,12 @@ -import type { DBConnection, UsernamePolicy, PasswordPolicy, TransactionContext } from '../../interfaces/models/transaction'; -import type { TransactionMembersOnLoginId } from '../../interfaces/screens/login-id'; -import type { TransactionMembersOnSignupId } from '../../interfaces/screens/signup-id'; -import type { IdentifierType } from '../../src/constants'; +import type { + DBConnection, + UsernamePolicy, + PasswordPolicy, + TransactionContext, +} from "../../interfaces/models/transaction"; +import type { TransactionMembersOnLoginId } from "../../interfaces/screens/login-id"; +import type { TransactionMembersOnSignupId } from "../../interfaces/screens/signup-id"; +import type { IdentifierType } from "../../src/constants"; /** * Checks if signup is enabled for the current connection. @@ -19,7 +24,9 @@ export function isSignupEnabled(transaction: TransactionContext): boolean { * @param transaction - The transaction context from Universal Login * @returns True if forgot password is enabled, false otherwise */ -export function isForgotPasswordEnabled(transaction: TransactionContext): boolean { +export function isForgotPasswordEnabled( + transaction: TransactionContext +): boolean { const connection = transaction?.connection as DBConnection; return connection?.options?.forgot_password_enabled === true; } @@ -53,7 +60,9 @@ export function isUsernameRequired(transaction: TransactionContext): boolean { * @param transaction - The transaction context from Universal Login * @returns The username policy object or null if not defined */ -export function getUsernamePolicy(transaction: TransactionContext): UsernamePolicy | null { +export function getUsernamePolicy( + transaction: TransactionContext +): UsernamePolicy | null { const connection = transaction?.connection as DBConnection; const validation = connection?.options?.attributes?.username?.validation; @@ -76,19 +85,34 @@ export function getUsernamePolicy(transaction: TransactionContext): UsernamePoli * @param transaction - The transaction context from Universal Login * @returns The password policy object or null if not defined */ -export function getPasswordPolicy(transaction: TransactionContext): PasswordPolicy | null { +export function getPasswordPolicy( + transaction: TransactionContext +): PasswordPolicy | null { const connection = transaction?.connection as DBConnection; const passwordPolicy = connection?.options?.authentication_methods?.password; - if (!passwordPolicy) return null; return { minLength: passwordPolicy.min_length, - policy: passwordPolicy.policy as PasswordPolicy['policy'], + policy: passwordPolicy.policy as PasswordPolicy["policy"], passwordSecurityInfo: passwordPolicy.password_security_info, }; } +/** + * Retrieves the password complexity configuration from the transaction context. + * This includes properties like required character types and length requirements. + * + * @param transaction - The transaction context from Universal Login + * @returns The password complexity object or null if not defined` + */ +export function getPasswordComplexity( + transaction: TransactionContext +): any | null { + const connection = transaction?.connection as DBConnection; + return connection?.options?.password_options?.complexity || null; +} + /** * Returns the allowed identifiers (email, username, phone) based on the connection settings. * This includes both required and optional identifier types. @@ -96,11 +120,13 @@ export function getPasswordPolicy(transaction: TransactionContext): PasswordPoli * @param transaction - The transaction context from Universal Login * @returns An array of allowed identifier types or null if none are defined */ -export function getAllowedIdentifiers(transaction: TransactionContext): TransactionMembersOnLoginId['allowedIdentifiers'] { +export function getAllowedIdentifiers( + transaction: TransactionContext +): TransactionMembersOnLoginId["allowedIdentifiers"] { const connection = transaction?.connection as DBConnection; if (!connection?.options?.attributes) return null; - return extractIdentifiersByStatus(connection, ['required', 'optional']); + return extractIdentifiersByStatus(connection, ["required", "optional"]); } /** @@ -109,8 +135,12 @@ export function getAllowedIdentifiers(transaction: TransactionContext): Transact * @param transaction - The transaction context from Universal Login * @returns An array of required identifier types or null if none are defined */ -export function getRequiredIdentifiers(transaction: TransactionContext): TransactionMembersOnSignupId['requiredIdentifiers'] { - return extractIdentifiersByStatus(transaction?.connection as DBConnection, ['required']); +export function getRequiredIdentifiers( + transaction: TransactionContext +): TransactionMembersOnSignupId["requiredIdentifiers"] { + return extractIdentifiersByStatus(transaction?.connection as DBConnection, [ + "required", + ]); } /** @@ -119,8 +149,12 @@ export function getRequiredIdentifiers(transaction: TransactionContext): Transac * @param transaction - The transaction context from Universal Login * @returns An array of optional identifier types or null if none are defined */ -export function getOptionalIdentifiers(transaction: TransactionContext): TransactionMembersOnSignupId['optionalIdentifiers'] { - return extractIdentifiersByStatus(transaction?.connection as DBConnection, ['optional']); +export function getOptionalIdentifiers( + transaction: TransactionContext +): TransactionMembersOnSignupId["optionalIdentifiers"] { + return extractIdentifiersByStatus(transaction?.connection as DBConnection, [ + "optional", + ]); } /** @@ -130,7 +164,9 @@ export function getOptionalIdentifiers(transaction: TransactionContext): Transac * @param transaction - The transaction context from Universal Login * @returns True if flexible identifiers are supported, false otherwise */ -export function hasFlexibleIdentifier(transaction: TransactionContext): boolean { +export function hasFlexibleIdentifier( + transaction: TransactionContext +): boolean { const connection = transaction.connection as DBConnection; return connection?.options?.attributes ? true : false; } @@ -143,14 +179,25 @@ export function hasFlexibleIdentifier(transaction: TransactionContext): boolean * @param statuses - Array of statuses to filter by ('required' or 'optional') * @returns Array of matching identifier types or null if none are found */ -function extractIdentifiersByStatus(connection: DBConnection | undefined, statuses: ('required' | 'optional')[]): IdentifierType[] | null { +function extractIdentifiersByStatus( + connection: DBConnection | undefined, + statuses: ("required" | "optional")[] +): IdentifierType[] | null { if (!connection?.options?.attributes) return null; return Object.entries(connection.options.attributes) - .filter(([, value]) => value.signup_status && statuses.includes(value.signup_status as 'required' | 'optional')) + .filter( + ([, value]) => + value.signup_status && + statuses.includes(value.signup_status as "required" | "optional") + ) .map(([key]) => key as IdentifierType).length > 0 ? Object.entries(connection.options.attributes) - .filter(([, value]) => value.signup_status && statuses.includes(value.signup_status as 'required' | 'optional')) + .filter( + ([, value]) => + value.signup_status && + statuses.includes(value.signup_status as "required" | "optional") + ) .map(([key]) => key as IdentifierType) : null; }