auth-user-component is a reusable component for authentication and authorization that can be used across all the apps developed by 101 Digital.
- Provide login function and keep the current session
- Auto-refresh token once the token is expired
- Include manage organization token
- Support login with OAuth2
To add this component to React Native app, run this command:
yarn add git+ssh://git@github.com/101digital/auth-user-component.git
Make sure you have permission to access this repository
Because auth-user-component depends on some libraries, so make sure you installed all dependencies into your project.
- react-native-theme-component
- react-native-app-auth. Note You need to see Stetup only. Other functions have been implemented.
- react-native-background-timer using for countdown
Before using this component, you must configure environment variables. That should be configure early in top of your app.ts
import { AuthComponent } from 'auth-user-component';
AuthComponent.instance()
.configure({
clientId: string;
clientSecret: string;
ternantDomain: string;
tokenBaseUrl: string;
membershipBaseUrl: string;
appGrantType?: string; // using for get app token
appScope?: string; // using for get app token
authGrantType?: string; // using for login
authScope?: string; // using for login,
redirectUrl?: string; // required for oauth2
authorizationBaseUrl?: string; // required for oauth2
revocationBaseUrl?: string; // required for oauth2
endSessionBaseUrl?: string; // required for oauth2
notificationBaseUrl?: string // required for push notification
})
.then(() => {
// init other component, such as Banking Component
// Ex:
// BankingService.getInstance().initClients({
// walletClient: createAuthorizedApiClient(wallet),
// openBankAispClient: createAuthorizedApiClient(openBankingAisp),
// openBankAuthClient: createAuthorizedApiClient(openBankingAuth),
});
});
const App = () => {
return (
<View>
<AuthProvider>/* YOUR COMPONENTS */</AuthProvider>
</View>
);
};
export default App;
auth-user-component also provides AuthContext
using Context API to maintain authentication state. If you want to use AuthContext
you HAVE TO wrap your components with AuthProvider
. This is required if you use LoginComponent
.
Create client to excute API request that only required basic token.
baseURL
: base url of services
import { createAppTokenApiClient } from 'auth-user-component';
const identityApiClient = createAppTokenApiClient(env.api.baseUrl.identity);
Create client to excute API requests that required Authentication
baseURL
: base url of serviceswithOrgToken
: this is OPTIONAL for request needorg-token
for Authentication
import { createAuthorizedApiClient } from 'auth-user-component';
const authApiClient = createAuthorizedApiClient(env.api.baseUrl.identity);
Maintain authentication state using Context API. To retrieve Context data and function, you can use useContext
inside a React Component.
import React, { useContext } from 'react';
import { AuthContext } from 'auth-user-component';
const ReactComponentExample = () => {
const { login, profile } = useContext(AuthContext);
/* YOUR COMPONENT */
};
- Functions and state
export interface AuthContextData {
profile?: Profile; // Current user profile. Return `undefined` if not authenticated
profilePicture?: string; // Get current profile picture
isSignedIn: boolean; // Authentication state. Return `true` if authenticated, or else return `false`
isSigning: boolean; // Return `true` if excuting login action
errorSignIn?: Error; // Return error value if any failures while excuting login
login: (username: string, password: string) => Promise<Profile | undefined>; // Execute login action
loginOAuth2: () => Promise<Profile | undefined>; // Execute login OAuth2
logout: () => void; // Excute logout action
clearSignInError: () => void; // Clear current failed login state
updateProfile: (
userId: string,
firstName: string,
lastName: string,
profilePicture?: string
) => Promise<boolean>; // Update current profile information
isUpdatingProfile?: boolean; // Updating profile state
errorUpdateProfile?: Error; // Error while update profile
clearUpdateProfileError: () => void;
registerDevice: (
token: string,
platform: 'IOS' | 'Android',
userId: string,
appId: string,
entityId: string
) => boolean; // registering device with fcm token for notification
}
Provide functions to make authentication
- Functions
Name | Type | Description |
---|---|---|
login | Function (username, password) | Promise function using username/password or email/password to generate token. If successfully, access_token and refresh_token will be stored to local storage. Then it will return response data. |
loginOAuth2 | Function | Promise function using OAuth2. The application will be redirect to Authorization Code Flow . If successfully, accessToken and refreshToken will be stored to local storage. Then it will return response data. |
refreshToken | Function (refresh_token) | Promise function to re-new token from old refresh_token . If successfully, access_token and refresh_token will be stored to local storage. Then it will return response data. |
fetchOrgToken | Function | Promise function to get token from organization which linked to accounts. If successfully, org_token will be stored to local storage. |
logout | Function | Promise function to clear current session |
fetchAppAccessToken | Function | Promise function return app access token base on basic token |
changeUserPassword | Function (currentPassword, newPassword,confirmNewPassword) | Promise function using currentPassword, newPassword and confirmNewPassword to change account password. |
registerDevice | Function (fcmToken, platform, userId, appId, entityId) | Function to register user deivce to get notification |
validateUserForgotPassword | Function (email, nric) | Promise function using email, nric to validate and start forgot password flow. |
import { AuthServices } from 'auth-user-component';
// your logic
const resp = await AuthServices.instance().login('username', 'password');
Provide functions to store and retrieve stored data in local storage
- Functions
Name | Type | Description |
---|---|---|
storeAccessToken | Function (access_token) | Store latest access token to local storage |
getAccessToken | Function | Retrieve latest access token from local storage |
storeRefreshToken | Function (refresh_token) | Store latest refresh token to local storage |
getRefreshToken | Function | Retrieve latest refresh token from local storage |
storeOrgToken | Function (org_token) | Store latest org token to local storage |
getOrgToken | Function | Retrieve latest org token from local storage |
storeProfile | Function (profile) | Store current profile data to local storage |
getProfile | Function | Retrieve current profile data from local storage |
clearAuths | Function | Clear current access_token, refresh_token, org_token, profile data |
import { authComponentStore } from 'auth-user-component';
If refresh token is failed (token full exipred), a callback function will be fired. You can listen that in your screen
import { AuthComponent } from 'auth-user-component';
useEffect(() => {
AuthComponent.instance().addSessionListener(handleSessionExpired);
return () => {
AuthComponent.instance().removeSessionListener(handleSessionExpired);
};
}, []);
const handleSessionExpired = () => {
// Call your logout function
};
Provide a simple login form (that is optional, you can use your login form), support type email
and phonenumber
. You can listen login succeed or failed response then handle your business logic.
Important: If you use LoginComponent, you HAVE TO wrap your App
with AuthProvider
import { LoginComponent, LoginComponentRef } from 'auth-user-component';
const LoginScreen = () => {
const loginRefs = useRef<LoginComponentRef>(); // use to update country code
const setCountryCode = (code: string) => {
loginRefs?.current?.updateCountryCode(code);
}
return (
<View>
/* YOUR COMPONENTS */
<LoginComponent
ref={loginRefs}
Root={{
props: {
onLoginSuccess: (userData) => {
// handle login success with profile data
},
onLoginFailed: (error) => {
// handle login failed with error
},
onPressForgotPassword: () => {
// handle click to forgot password
},
onPressRegister: () => {
// handle click to register
},
formatError: getErrorMessage, // format in-line error message, ex translate error to language
}
}}
/>
/* YOUR COMPONENTS */
</View>
);
}
props
Name | Type | Description |
---|---|---|
onLoginSuccess | Function (Required) | Return user profile data if login successfully |
onLoginFailed | Function (Required) | Return login error |
onPressForgotPassword | Function (Required) | Handle actions when forgot password button clicked |
onPressRegister | Function (Required) | Handle actions when sign up now button clicked |
formatError | Function (Optional) | Format in-line error message, example translate error message |
formTitle | string (Optional) | Label of login form (default is Sign In ) |
loginButtonLabel | string (Optional) | Label of login button (default is Login ) |
notAccountLabel | string (Optional) | Label of no account message (default is Not a user yet? ) |
signUpLabel | string (Optional) | Label of register button (default is Sign up here ) |
forgotPasswordLabel | string (Optional) | Label of forgot password button (default is Forgot password ) |
style
Type of LoginComponentStyles
Name | Type | Description |
---|---|---|
containerStyle | ViewStyle | Wrapper styles of component |
formTitleStyle | TextStyle | Style of login form title |
forgotPasswordContainerStyle | ViewStyle | Style of forgot password button |
forgotPasswordLabelStyle | TextStyle | Style of forgot password label |
noneAccountLabelStyle | TextStyle | Style of no account label |
signUpLabelStyle | TextStyle | Style of register label |
signUpContainerStyle | ViewStyle | Style of register button |
components
Name | Type | Description |
---|---|---|
header | ReactNode (Optional) | Header of component |
footer | ReactNode (Optional) | Footer of component |
renderForgotPasswordButton | Function return React Element | Override default forgot password button |
renderRegisterButton | Function return React Element | Override default register button |
props
Name | Type | Description |
---|---|---|
initialSignInData | SignInData (Optional) | Initial sign in data, default is empty username and password |
type | email or phonenumber (Optional) |
Type of login form, default is phonenumber |
validationSchema | Yup scheme (Optional) | Validation for username and password , base on your business logic |
usernameHint | string (Optional) | Username placeholder text |
passwordHint | string (Optional) | Password placeholder text |
onPressDialCode | Function (Optional) | Handle actions when click dial code label |
style
Type of InputFormStyles
Name | Type | Description |
---|---|---|
userNameInputFieldStyle | InputPhoneNumberStyles (Optional) | Styles of username input field |
passwordInputFieldStyle | InputFieldStyles (Optional) | Styles of password input field |
More about styles of each, you can reference here: https://github.com/101digital/react-native-theme-component.git
component
Name | Type | Description |
---|---|---|
passwordIcon | ReactNode (Optional) | Prefix icon of password field |
usernameIcon | ReactNode (Optional) | Prefix icon of username field |
-
Make sure you synced latest local data in
auth-component.json
into auth-component.json. They should be synced once you update -
Add the auth-component to
components
tags and replate[data]
with your values. The auth-component havecomponentId
is "72520fc5-6be5-4ee5-b986-0e688ab4adff" and it can't be changed.
{
...
"components": [
{
"componentId": "72520fc5-6be5-4ee5-b986-0e688ab4adff",
"name": "AuthComponent",
"isRequired": true,
"config": {
"clientId": "[data]",
"clientSecret": "[data]",
"ternantDomain": "[data]",
"tokenBaseUrl": "[data]",
"membershipBaseUrl": "[data]"
}
}
]
...
}
- Check required dependencies of auth-component inside tag
dependencies
inconfig.json
. Make sure tagdependencies
must have enough below data
{
...
"dependencies": [
{ "name": "https://github.com/101digital/react-native-theme-component.git" },
]
...
}
If have any item is not existing in dependencies
of config.json
file, please find missing one from src/component.json
and put it to dependencies
.
- Place
LoginComponent
with one template to the Screen
TemplateID | Template name |
---|---|
7c795b14-8ae4-47e0-9a94-162ff71bdf77 | Login with email template |
f7244753-8d95-4f91-a1fa-88df174dc064 | Login with phone number template |
- Example, if you want to place
LoginComponent
with email template toLoginScreen
, then if user triggeronPressForgotPassword
button, and you wanna navigate toForgotPasswordScreen
Note that: templateId
is one of template defined in src/component.json
. ForgotPasswordScreen
is existing with route
name is forgot-password-screen
{
...
"screens": [
{
"screenName": "LoginScreen",
"route": "login-screen",
"stack": "auth-navigator",
"screenParams": [],
"components": [
{
"templateId": "7c795b14-8ae4-47e0-9a94-162ff71bdf77",
"componentName": "LoginComponent",
"functions": [
{
"id": "c23dc9e4-5a70-41e7-bc30-83e9fdad3cac",
"name": "onPressForgotPassword",
"action": {
"type": "openScreen",
"route": "forgot-password-screen"
}
},
{
"id": "01c73fba-7060-49ec-acd2-2afbf3e7fda2",
"name": "onPressRegister",
"action": {
"type": "openScreen",
"route": "register-screen"
}
}
]
}
]
}
]
...
}
Provide a simple function to handle the change password .
Important: If you use ChangePasswordComponent, you HAVE TO wrap your App
with AuthProvider
import { ChangePassword, ChangePasswordRef,PasswordMask } from 'auth-user-component';
const ChangePasswordScreen = () => {
const passwordRefs = useRef<ChangePasswordRef>();
return (
<View>
/* YOUR COMPONENTS */
<ChangePassword
ref={passwordRefs}
Root={{
props: {
onPressBack: () => {
// handle header back icon
},
onPress: () => {
handle success response
},
title: i18n?.t('change_password.lbl_change_password'),
subTitle: i18n?.t('change_password.lbl_sub_title'),
},
components: {
header: <View />,
},
}}
InputForm={{
component: {
passwordIcon: <View />,
usernameIcon: <View />,
suffixIcon: (
<PasswordMask
isVisible={isVisiblePassword}
onPress={() => setVisiblePassword(!isVisiblePassword)}
/>
),
newSuffixIcon: (
<PasswordMask
isVisible={isNewVisiblePassword}
onPress={() => setNewVisiblePassword(!isNewVisiblePassword)}
/>
),
confirmSuffixIcon: (
<PasswordMask
isVisible={isConfirmVisiblePassword}
onPress={() => setConfirmVisiblePassword(!isConfirmVisiblePassword)}
/>
),
},
style: {
passwordInputFieldStyle: {
contentContainerStyle: {
backgroundColor: '#fff',
},
},
checkBoxInputFieldStyle: {
selectedBoxStyle: {
width: 20,
height: 20,
borderRadius: 4,
backgroundColor: '#14BDEB',
alignItems: 'center',
justifyContent: 'center',
},
unSelectedBoxStyle: {
width: 20,
height: 20,
borderRadius: 4,
borderWidth: 1,
borderColor: '#14BDEB',
alignItems: 'center',
justifyContent: 'center',
},
titleStyle: {
flex: 1,
fontSize: 12,
color: '#000000',
marginLeft: 12,
lineHeight: 21,
},
containerStyle: {
flexDirection: 'row',
alignItems: 'center',
width: '85%',
},
},
},
props: {
onPressDialCode: () => {},
withDialCode: false,
withLabel: true,
isVisiblePassword: isVisiblePassword,
isNewVisiblePassword: isNewVisiblePassword,
isConfirmVisiblePassword: isConfirmVisiblePassword,
},
}}
/>
/* YOUR COMPONENTS */
</View>
);
}
The component provides a form to input old, new password and confirm new password to verify user old password for change password.
import {
InputPhoneNumberComponent,
InputPhoneNumberComponentRef,
} from 'auth-user-component';
const RecoveryPasswordScreen = () => {
const inputPhoneNumberRefs = useRef<InputPhoneNumberComponentRef>();
return (
<View>
/* YOUR COMPONENTS */
<InputPhoneNumberComponent
ref={inputPhoneNumberRefs}
Root={{
props: {
onVerifyPhoneNumberSuccess:(recoveryData) => {
// handle verify phone number success with recovery data
}
onVerifyPhoneNumberFailed:(error) => {
// handle verify phone number failed with error data
}
onPressBack:() => {
// handle press back arrow button
}
},
}}
InputForm={{
props: {
validationSchema: $MobilePhoneSchema,
},
}}
/>
/* YOUR COMPONENTS */
</View>
);
};
The component provides a OTP input field, new password and confirm new password to verify user old password for change password.
import { colors, OtpVerification, OtpVerificationComponentRef } from 'auth-user-component';
export type ResetPasswordOtpVerificationParams = {};
const ResetPasswordOtpVerificationScreen = () => {
const otpVerificationRefs = useRef<OtpVerificationComponentRef>();
return (
<OtpVerification
ref={otpVerificationRefs}
Root={{
props: {
onConfirmPasswordError: () => {
// handle error confirm password action
},
onPressBack: () => {
// handle press back arrow button
},
onVerifyOTPSuccess: () => {
// handle success verify otp action
},
onVerifyOTPFailed: () => {
// handle failed verify otp action
},
},
}}
/>
);
};