Class

AuthApi

AuthApi()

Authentication Hooks. Instance name: authApi

It enables the app to have authentication options during the signup/login process.

Constructor

# new AuthApi()

Example
externalCodeSetup.authApi.METHOD_NAME

Members

# setUserAgreementModalComponent

You can use this hook to customize the User Agreement modal. For example, you can add a checkbox at the bottom of the modal which will require users to scroll to the bottom to agree to the Terms of Service.

Example

Use default BB implementation and add the User Agreement Text inside the modal

//In custom_code/components/UserAgreementModal.js...

import React from "react";
import { View, Text, Platform } from "react-native";
import { wrapHtml } from "@src/utils";
import { DEVICE_HEIGHT, globalStyle } from "@src/styles/global";
import ScrollableModal from "@src/components/Modals/ScrollableModal";
import BottomSheetCloseButton from "@src/components/BottomSheet/BottomSheetCloseButton";
import ContentPlaceholder from "@src/components/ContentPlaceholder";
import ResizingWebView from "@src/components/ResizingWebView";
import { generateAssetsFontCss } from "@src/utils/jsUtils";
import { UserAgreementText } from "@src/components/Auth/UserAgreementText";

const UserAgreementModal = (props) => {
  const {
    config,
    onClosed,
    modalContent,
    modalHeader,
    hideModal,
    refModal,
    colors,
    global,
    t,
    termsOfService,
    privacyPolicy,
    onPress,
    onAgreementChecked,
    agreementChecked,
  } = props;

  const {
    htmlAdjustedCss,
    htmlContentCss,
    htmlStylesCss,
    typography,
    headerStyle
  } = globalStyle(config.styles);

  const renderModalHeader = () => (
    <View
      style={[
        global.row,
        global.panelHeader,
        headerStyle,
        { borderColor: colors.borderColor, borderBottomWidth: 0.5 }
      ]}
    >
      {!!modalHeader && <Text style={global.filterTitle}>{modalHeader}</Text>}

      <BottomSheetCloseButton onClose={hideModal} colors={colors} />
    </View>
  );

  return (
    <ScrollableModal
      ref={refModal}
      onClosed={onClosed}
      HeaderComponent={renderModalHeader(global, colors)}
      adjustToContentHeight={false}
      scrollViewProps={{ backgroundColor: colors.bodyFrontBg }}
    >
      <View style={{flex: 1, padding: 20, borderRadius: 12}}>
        <UserAgreementText
          {...{
            register: true,
            colors,
            global,
            t,
            termsOfService,
            privacyPolicy,
            withCheckbox: true,
            onPress,
            onAgreementChecked,
            agreementChecked
          }}
        />
        <ResizingWebView
          defaultHeight={DEVICE_HEIGHT}
          width={"100%"}
          startInLoadingState={true}
          scrollEnabled={Platform.OS === "android"}
          webViewStyle={Platform.OS === "android" ? { marginBottom: 150 } : {}}
          nestedScrollEnabled
          autoHeight={Platform.OS === "ios"}
          fixAndroid={true}
          contentId={"userAgreementContent"}
          source={{
            html: wrapHtml(
              String(htmlStylesCss).replace(";overflow: hidden", "") +
              htmlAdjustedCss +
              htmlContentCss +
              generateAssetsFontCss(
                typography.bodyText.family,
                typography.bodyText.type
              )
            )(modalContent)
          }}
          renderLoading={() => (
            <View
              style={{
                position: "absolute",
                top: 0,
                left: 0,
                bottom: 0,
                right: 0,
                zIndex: 1
              }}
            >
              <ContentPlaceholder global={global} base={colors.headingsColor} />
            </View>
          )}
        />
      </View>
    </ScrollableModal>
  );
};

export default UserAgreementModal;

//In custom_code/index.js...

...

import UserAgreeementModal from "./components/UserAgreementModal"
export const applyCustomCode = externalCodeSetup => {
  externalCodeSetup.authApi.setUserAgreementModalComponent(props => <UserAgreeementModal {...props} />)
}

Methods

# setLoginButton(LoginButton)

You can use this hook to replace the Login Button on the login screen. You can also use this hook to change the behavior or styling of the login button on the login screen

Parameters:
Name Type Description
LoginButton React.ComponentType.<LoginButtonProps>
Example

Add an extra validation step before logging-in a user

...

import {Alert} from "react-native"
import AppButton from "@src/components/AppButton";

export const applyCustomCode = externalCodeSetup => {
    externalCodeSetup.authApi.setLoginButton(props => {
        const {
            t,
            global,
            colors,
            doLogin,
            stateUsername,
            statePassword,
            auth
        } = props;

        const customLoginAction = () => {
            let isValid = false;

            //Call an action to do extra validation such as an OTP request...
            isValid = false;

            //If successful, do default app login...
            if (isValid) doLogin();
            else Alert.alert("OTP is invalid");
        };

        return (
            <AppButton
                accessibilityLabel="login_button"
                accessible
                style={[{marginTop: 10}, global.authButtonContainer]}
                onPress={() => customLoginAction()}
                label={t("login:login")}
                global={global}
                loading={auth.isFetching}
                labelStyle={global.authButtonLabel}
                spinnerColor={colors.authButtonTextColor}
            />
        );
    });
}

You can use this hook to replace the logo in the Login Screen. For example, you can change the logo's image, dimensions, or position.

Parameters:
Name Type Description
LoginLogo React.ComponentType.<LoginLogoProps>
Example
...

import AppImage from "@src/components/AppImage";
export const applyCustomCode = (externalCodeSetup: ExternalCodeSetup) => {

    externalCodeSetup.authApi.setLoginLogo(({
        hideLogo,
        headerStyle,
        logoStyle,
        source
    }) => {

        return !hideLogo && (
            <View style={headerStyle}>
                <AppImage
                    id="logo"
                    resizeMode={"contain"}
                    style={logoStyle}
                    source={source}
                />
            </View>
        )
    })
}

# setRenderAuthInputs(renderAuthInput)

You can use this hook to modify the authentication fields on the login screen. For example, add a 'Phone' field along with the default email and password fields on the login screen.

Parameters:
Name Type Description
renderAuthInput React.ComponentType.<RenderAuthInputsProps>
Examples

Add a phone field along with username and password

//In custom_code/LoginCustom.js...

import React, { useEffect, useState, useRef } from 'react';
import { Alert } from "react-native"
import AppButton from "@src/components/AppButton";
import TextInput from "@src/components/AppInput";
import { getExternalCodeSetup } from '../../src/externalCode/externalRepo';

const externalCodeSetup = getExternalCodeSetup();

const phoneIcon = require("../assets/img/phone.png");

const customLoginAction = (doLogin) => {

    let isValid = false;

    //Call an action to do extra validation such as an OTP request...

    isValid = true;

    //If successful, do default app login...
    if (isValid)
        doLogin()
    else
        Alert.alert("OTP is invalid")
}

const renderLoginButton = (phoneRef, usernameRef, passwordRef) => {

    externalCodeSetup.authApi.setLoginButton(props => {
        const {
            t,
            global,
            stateUsername,
            statePassword,
            isSignUpEnabled,
            privacyPolicy,
            termsOfService,
            withUserAgreementCheckbox,
            stateAgreementChecked,
            passwordInput,
            usernameInput,
            doLogin,
            auth,
            colors
        } = props;

        return <AppButton
            accessibilityLabel="login_button"
            accessible
            style={[{ marginTop: 10 }, global.authButtonContainer]}
            onPress={() => {
                if (!!!stateUsername || !!!statePassword) {
                    return false;
                }
                if (
                    !isSignUpEnabled &&
                    withUserAgreementCheckbox &&
                    (privacyPolicy || termsOfService) &&
                    !stateAgreementChecked
                ) {
                    return Alert.alert(
                        t("login:user_agreement_title"),
                        t(
                            `login:user_agreement_message${privacyPolicy ? "_privacy" : ""
                            }${termsOfService ? "_terms" : ""}`
                        )
                    );
                }
                phoneRef.current.clear()
                usernameRef.current.clear()
                passwordRef.current.clear()
                customLoginAction(doLogin);
            }}
            label="Get OTP"
            global={global}
            loading={auth.isFetching}
            labelStyle={global.authButtonLabel}
            spinnerColor={colors.authButtonTextColor}
        />
    });
}

const AuthInputs = props => {
    const {
        t,
        authBg,
        global,
        colors,
        backgroundColor,
        textColor,
        userIcon,
        setUsernameState,
        stateUsername,
        placeholderColor,
        calcFontSize,
        passwordIcon,
        setPasswordState,
        statePassword,
        doLogin,
        auth
    } = props;

    const [phone, setPhone] = useState('');
    const phoneRef = useRef(null);
    const usernameRef = useRef(null);
    const passwordRef = useRef(null);

    useEffect(() => {
        renderLoginButton(phoneRef, usernameRef, passwordRef);
    }, [])

    return <>
        <TextInput
            accessibilityLabel="login_phone_input"
            accessible
            ref={phoneRef}
            bodyColor={authBg}
            inputWrapStyle={[global.formInput, { backgroundColor }]}
            style={[
                global.formInputText,
                { color: textColor, paddingLeft: 0 }
            ]}
            keyboardType="numeric"
            icon={phoneIcon}
            webIcon={"IconUser"}
            tintColor={textColor}
            onChangeText={phone => setPhone({ phone })}
            value={phone}
            placeholderTextColor={placeholderColor}
            placeholder="Phone"
            autoCapitalize={"none"}
            returnKeyType="next"
            underlineColorAndroid="transparent"
            autoCorrect={false}
            calcFontSize={calcFontSize}
        />
        <TextInput
            accessibilityLabel="login_username_input"
            accessible
            ref={usernameRef}
            bodyColor={authBg}
            inputWrapStyle={[global.formInput, { backgroundColor }]}
            style={[
                global.formInputText,
                { color: textColor, paddingLeft: 0 }
            ]}
            icon={userIcon}
            webIcon={"IconUser"}
            tintColor={textColor}
            onChangeText={username => setUsernameState({ username })}
            value={stateUsername}
            placeholderTextColor={placeholderColor}
            placeholder={t("login:username")}
            autoCapitalize={"none"}
            returnKeyType="next"
            underlineColorAndroid="transparent"
            autoCorrect={false}
            calcFontSize={calcFontSize}
        />

        <TextInput
            accessibilityLabel="login_password_input"
            accessible
            bodyColor={authBg}
            inputWrapStyle={global.inputWrap}
            inputWrapStyle={[global.formInput, { backgroundColor }]}
            style={[
                global.formInputText,
                { color: textColor, width: "70%", paddingLeft: 0 }
            ]}
            ref={passwordRef}
            icon={passwordIcon}
            webIcon={"IconLock"}
            eyeTintColor={textColor}
            tintColor={textColor}
            onChangeText={password => setPasswordState({ password })}
            value={statePassword}
            placeholderTextColor={placeholderColor}
            placeholder={t("login:password")}
            secureTextEntry={true}
            secureToggle={true}
            returnKeyType={"done"}
            autoCapitalize={"none"}
            underlineColorAndroid="transparent"
            autoCorrect={false}
            onSubmitEditing={() =>
                // done key should execute action
                customLoginAction(doLogin)
            }
            calcFontSize={calcFontSize}
        />

    </>

}

export default AuthInputs;

//In custom_code/index.js...

import LoginCustom from "./components/LoginCustom";
export const applyCustomCode = externalCodeSetup => {
  externalCodeSetup.authApi.setRenderAuthInputs(props => <LoginCustom  {...props} />)
}

Use default implementation of auth inputs in a hook

export const applyCustomCode = externalCodeSetup => {

 externalCodeSetup.authApi.setRenderAuthInputs((props) => {

   const {
     t,
     authBg,
     global,
     colors,
     backgroundColor,
     textColor,
     userIcon,
     setUsernameState,
     stateUsername,
     placeholderColor,
     calcFontSize,
     passwordIcon,
     setPasswordState,
     statePassword,
     doLogin,
     setInputRef,
     auth
   } = props;

   let passwordRef;

   const setPasswordRef = (component) => {

     //Set password ref for LoginScreen.js
     setInputRef("password", component);

     //Set password ref for this component
     passwordRef = component;
   }

   return <>
     <TextInput
       accessibilityLabel="login_username_input"
       accessible
       ref={component => setInputRef("username", component)}
       bodyColor={authBg}
       inputWrapStyle={[global.formInput, { backgroundColor }]}
       style={[
         global.formInputText,
         { color: textColor, paddingLeft: 0 }
       ]}
       tabIndex={1}
       icon={userIcon}
       webIcon={"IconUser"}
       tintColor={textColor}
       onChangeText={username => setUsernameState({ username })}
       value={stateUsername}
       placeholderTextColor={placeholderColor}
       placeholder={t("login:username")}
       autoCapitalize={"none"}
       returnKeyType="next"
       underlineColorAndroid="transparent"
       autoCorrect={false}
       onSubmitEditing={() => {
         // next key should focus on next input by default
         passwordRef.focus()
       }}
       calcFontSize={calcFontSize}
     />

     <TextInput
       accessibilityLabel="login_password_input"
       accessible
       bodyColor={authBg}
       inputWrapStyle={global.inputWrap}
       inputWrapStyle={[global.formInput, { backgroundColor }]}
       style={[
         global.formInputText,
         { color: textColor, width: "70%", paddingLeft: 0 }
       ]}
       ref={component => setPasswordRef(component) }
       tabIndex={2}
       icon={passwordIcon}
       webIcon={"IconLock"}
       eyeTintColor={textColor}
       tintColor={textColor}
       onChangeText={password => setPasswordState({ password })}
       value={statePassword}
       placeholderTextColor={placeholderColor}
       placeholder={t("login:password")}
       secureTextEntry={true}
       secureToggle={true}
       returnKeyType={"done"}
       autoCapitalize={"none"}
       underlineColorAndroid="transparent"
       autoCorrect={false}
       onSubmitEditing={() =>
         // done key should execute action
         doLogin()
       }
       calcFontSize={calcFontSize}
     />
   </>
 })
}

# setSignupButton(SignupButton)

You can use this hook to replace the Signup Button on the Signup screen. You can also use this hook to change the behavior or styling of the signup button on the login screen.

Parameters:
Name Type Description
SignupButton React.ComponentType.<SignupButtonProps>
Example
...

import { Alert } from "react-native"
import AppButton from "@src/components/AppButton";
 export const applyCustomCode = externalCodeSetup => {

  externalCodeSetup.authApi.setSignupButton(props => {

    const {
      disabled,
      global,
      doSignup,
      label,
      loading,
      spinnerColor,
      visibleFields,
      profileType,
      fieldValues,
      formState,
      getValues
    } = props;

    const signupAction = () => {
      Alert.alert("OTP", `We will send an OTP to ${getValues.field_1}. Do you want to proceed?`, [
        {
          text: "Cancel",
          onPress: () => console.log("Cancel Pressed"),
          style: "cancel"
        },
        { text: "OK", onPress: () => console.log("OK Pressed") }
      ])
    }

    return <AppButton
      disabled={disabled}
      style={[{ marginTop: 17 }, global.regButtonContainer]}
      // onPress={doSignup}
      onPress={signupAction}
      label={label}
      global={global}
      loading={loading}
      labelStyle={[
        global.buttonLabel,
        global.regButtonLabel,
        disabled && { opacity: 0.4 }
      ]}
      spinnerColor={spinnerColor}
    />
  });

}

# setSignUpInputComponent(SignUpInputComponent)

You can use this hook to replace input components in the sign-up form.

Parameters:
Name Type Description
SignUpInputComponent React.ComponentType.<SignUpInputComponentProps>
Example
//In custom_code/index.js...
...

import MyTextInput from "./components/MyTextInput";

export const applyCustomCode = (externalCodeSetup: any) => {

    externalCodeSetup.authApi.setSignUpInputComponent(props => {
        const {DefaultComponent, label} = props;

        if (label === "Email") {
            return <MyTextInput {...props}/>
        }

        return DefaultComponent;
    });
}


 //In custom_code/components/MyTextInput.js...

import React, {useState} from "react";
import {View, TextInput, ActivityIndicator, Text} from "react-native";
import axios from "axios";
const MyTextInput = props => {
    const [loading, setLoading] = useState(false);
    const [data, setData] = useState(null);

    const fetchData = async text => {
        const search = text && text.toLowerCase();

        try {
            setLoading(true);
            const response = await axios.get(`https://example.api/${search}`);
            setData(response.data);
            setLoading(false);
        } catch (error) {
            console.error(error);
        }
    };

    const handleTextChange = text => {
        fetchData(text);
        props.setValue(props.field.id, text);
    };

    return (
        <View>
            <TextInput
                onChangeText={handleTextChange}
                placeholder="Type something..."
            />
            {loading && <ActivityIndicator />}
            {data && (
                <Text>
                    Result: {data.name} | {data.abilities[0].ability.name}
                </Text>
            )}
        </View>
    );
};

export default MyTextInput;

# setSignUpInputsFilter(func)

You can use it to set the function that can filter the input fields to modify the signup process based on your app needs.

Parameters:
Name Type Description
func SignupFieldsFilterCallback
Example

Example: Remove first field in the sign up form

externalCodeSetup.authApi.setSignUpInputsFilter(props => {
 props.shift()
 return props;
})

# setSignUpInputsValidation(SignUpInputsValidationCallback)

You can use this hook to set the validation rules for the sign-up form. For example, you can exempt a field so that it will not be required when submitting the form.

Parameters:
Name Type Description
SignUpInputsValidationCallback SignUpInputsValidationCallback
Example
authApi.setSignUpInputsValidation(props => {
  const {doValidation, fieldName, field, register, t} = props;

  const changeLabel = false;
  const exemptField = false;

  if (changeLabel) {
      return register(fieldName, {
          required:
              field.required &&
              field.required &&
              `Custom - ${field.label}` + t("signup:validateFeildModal"),
          validate: value => {
              const {message} = getFieldError(field, value, t);
              return message ?? true;
          }
      });
  }

  if (exemptField) {
      if (fieldName === "signup_email") {
          return;
      }
  }

  return doValidation()
});

You can use this hook to replace the sign up link component in the Login Screen.

Parameters:
Name Type Description
SignUpLinkProps React.ComponentType.<SignUpLinkProps>
Example
import React from "react";
import {View, Text} from "react-native";
import Icon from "@src/components/Icon";
import AppTouchableOpacity from "@src/components/AppTouchableOpacity";

export const applyCustomCode = externalCodeSetup => {
    externalCodeSetup.authApi.setSignUpLink(props => {
        const {isSignUpEnabled, footerRef, onPress, styles, t, colors} = props;

        return (
            isSignUpEnabled && (
                <AppTouchableOpacity
                    ref={footerRef}
                    onPress={onPress}
                    style={styles.buttonContainer}
                    hitSlop={{top: 15, right: 15, bottom: 15, left: 15}}
                >
                    <View style={styles.viewContainer}>
                        <Text style={styles.text}>
                            <Text>{t("login:haveAccount")}</Text>
                            <Text> </Text>
                            <Text style={styles.signUpText}>{t("login:signup")}</Text>
                        </Text>
                        <Icon
                            icon={{fontIconName: "arrow-right", weight: 300}}
                            webIcon={"IconArrowBack"}
                            tintColor={colors.authTextColor}
                            styles={{height: 15}}
                        />
                    </View>
                </AppTouchableOpacity>
            )
        );
    });
};

# setUserAgreementTextComponent(UserAgreementTextComponent)

You can use this hook to customize the User Agreement text component located before the "Create Account" button.

Parameters:
Name Type Description
UserAgreementTextComponent React.ComponentType.<UserAgreementTextComponentProps>
Example

Add an alert confirmation if the user has read the Terms of Service

//In custom_code/components/UserAgreementText.js...

import React from "react";
import {Text, View, Alert} from "react-native";
import AppCheckbox from "@src/components/AppCheckbox";
import {Check} from "@src/components/AppCheckbox";
import {getAuthFormTheme, getRegFormTheme} from "@src/components/Forms/utils";

const confirmAgreement = (onAgreementChecked) => {

    Alert.alert(
        "Confirmation",
        "Have you read the Terms of Service?",
        [
          {
            text: "No",
            onPress: () => onAgreementChecked(false),
          },
          { text: "Yes", onPress: () => onAgreementChecked(true) }
        ]
      );

}

const UserAgreementText = props => {
  const {
    register,
    t,
    termsOfService,
    privacyPolicy,
    withCheckbox,
    global,
    colors,
    onPress,
    agreementChecked,
    onAgreementChecked,
    style = {}
  } = props;

  if (!termsOfService?.content && !privacyPolicy?.content) return null;

  const theme = register ? getRegFormTheme(colors) : getAuthFormTheme(colors);
  const {tint, inactiveTint, textColor, labelColor} = theme;

  const tColor = withCheckbox ? textColor : labelColor;

  const containerStyle = withCheckbox && {
    borderRadius: 10,
    backgroundColor: theme.backgroundColor,
    paddingVertical: 14,
    paddingHorizontal: 16,
    marginBottom: 15,
    ...style
  };

  return (
    <View
      style={[
        global.row,
        {alignItems: "center", justifyContent: "center"},
        containerStyle
      ]}
    >
      <Text
        style={[
          global.text,
          {
            textAlign: withCheckbox ? "left" : "center",
            marginTop: withCheckbox ? 0 : 16,
            paddingRight: 4,
            paddingLeft: withCheckbox ? 0 : 4,
            color: tColor,
            flex: 1
          },
          withCheckbox && {
            marginRight: 5
          }
        ]}
      >
        {withCheckbox ? t(`signup:agreementStartingTextWithCheckbox`) : null}
        {!!termsOfService?.content ? (
          <Text
            style={[global.boldText, {color: tColor}]}
            onPress={() =>
              onPress("termsOfService", termsOfService.content.rendered)
            }
          >
            {t("signup:termsOfService")}
          </Text>
        ) : (
          ""
        )}
        {!!termsOfService?.content && privacyPolicy?.content
          ? t("signup:and")
          : ""}
        {!!privacyPolicy?.content ? (
          <Text
            style={[global.boldText, {color: tColor}]}
            onPress={() =>
              onPress("privacyPolicy", privacyPolicy.content.rendered)
            }
          >
            {t("signup:privacyPolicy")}
          </Text>
        ) : (
          ""
        )}
        {withCheckbox ? t("signup:agreementEndingText") : null}
      </Text>
      {withCheckbox && (
        <Check
          inactiveTint={inactiveTint}
          tintColor={tint}
          isChecked={agreementChecked}
          onPress={() => confirmAgreement(onAgreementChecked)}
          // onPress={() => onAgreementChecked(!agreementChecked)}
        />
      )}
    </View>
  );
};

export default UserAgreementText;

//In custom_code/index.js...

import UserAgreementText from "./components/UserAgreementText";
export const applyCustomCode = externalCodeSetup => {
 externalCodeSetup.authApi.setUserAgreementTextComponent(props => <UserAgreementText {...props} />);
}