Google SSO | Flow Revamp (#325)

* TP-27854 | SSO revamp

* TP-27854 | Config changes

* TP-27854 | Handle signout

* TP-27854 | Contract update

* TP-27854 | Fix corner cases

* TP-27854 | Corner cases fix

* TP-27854 | fix
This commit is contained in:
Himanshu Kansal
2023-05-09 16:20:12 +05:30
committed by GitHub Enterprise
parent fe43b12f20
commit cc3a7d6dda
13 changed files with 162 additions and 178 deletions

View File

@@ -1,5 +1,4 @@
apply plugin: "com.android.application"
apply plugin: 'com.google.gms.google-services'
apply plugin: "com.google.firebase.crashlytics"
import com.android.build.OutputFile
import org.apache.tools.ant.taskdefs.condition.Os
@@ -131,8 +130,8 @@ def reactNativeArchitectures() {
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
def VERSION_CODE = 57
def VERSION_NAME = "2.1.16"
def VERSION_CODE = 58
def VERSION_NAME = "2.1.17"
android {
ndkVersion rootProject.ext.ndkVersion
@@ -325,3 +324,4 @@ def isNewArchitectureEnabled() {
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}
apply plugin: 'com.google.gms.google-services'

View File

@@ -1,39 +1,39 @@
{
"project_info": {
"project_number": "60755663443",
"project_id": "address-verification-app",
"storage_bucket": "address-verification-app.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:60755663443:android:988149d3da3c00d38584a6",
"android_client_info": {
"package_name": "com.avapp"
}
},
"oauth_client": [
{
"client_id": "60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyA70_d2M2ke-Mu0OHGZ6iZilBbD6A-_z0c"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com",
"client_type": 3
}
]
}
"project_info": {
"project_number": "60755663443",
"project_id": "address-verification-app",
"storage_bucket": "address-verification-app.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:60755663443:android:988149d3da3c00d38584a6",
"android_client_info": {
"package_name": "com.avapp"
}
},
"oauth_client": [
{
"client_id": "60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyA70_d2M2ke-Mu0OHGZ6iZilBbD6A-_z0c"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
],
"configuration_version": "1"
}
}
],
"configuration_version": "1"
}

View File

@@ -7,6 +7,7 @@ buildscript {
minSdkVersion = 21
compileSdkVersion = 31
targetSdkVersion = 31
googlePlayServicesAuthVersion = "19.2.0"
if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64

View File

@@ -3,6 +3,8 @@ export const SENTRY_DSN =
'https://acef93c884c1424cacc4ec899562e203@qa-longhorn-portal.np.navi-tech.in/glitchtip-events/173';
export const JANUS_SERVICE_URL = 'https://dev-longhorn-portal.np.navi-tech.in/api/events/json';
export const ENV = 'dev';
export const IS_SSO_ENABLED = false;
export const IS_SSO_ENABLED = true;
export const APM_APP_NAME = 'cosmos-app';
export const APM_BASE_URL = 'https://dev-longhorn-portal.np.navi-tech.in/apm-events';
export const GOOGLE_SSO_CLIENT_ID =
'60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com';

View File

@@ -10,3 +10,5 @@ export const APM_APP_NAME = 'cosmos-app';
export const APM_BASE_URL = 'https://longhorn.navi.com/apm-events';
export const IS_DATA_SYNC_REQUIRED = true;
export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_A_MINUTE; // 2hr
export const GOOGLE_SSO_CLIENT_ID =
'136591056725-ev8db4hrlud2m23n0o03or3cmmp3a3cq.apps.googleusercontent.com';

View File

@@ -5,8 +5,10 @@ export const SENTRY_DSN =
'https://acef93c884c1424cacc4ec899562e203@qa-longhorn-portal.np.navi-tech.in/glitchtip-events/173';
export const JANUS_SERVICE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/api/events/json';
export const ENV = 'qa';
export const IS_SSO_ENABLED = false;
export const IS_SSO_ENABLED = true;
export const APM_APP_NAME = 'cosmos-app';
export const APM_BASE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/apm-events';
export const IS_DATA_SYNC_REQUIRED = true;
export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_A_MINUTE; // 2hr
export const GOOGLE_SSO_CLIENT_ID =
'60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com';

View File

@@ -1,39 +1,39 @@
{
"project_info": {
"project_number": "1073868858509",
"project_id": "qa-address-verification-app",
"storage_bucket": "qa-address-verification-app.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:1073868858509:android:1cc386054733f020d7eec4",
"android_client_info": {
"package_name": "com.avapp"
}
},
"oauth_client": [
{
"client_id": "1073868858509-eumumh6fhdkcc1spm465itqnepp7uc5c.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAArgdF-UGxr4kaLnzXjVhgLQTcbyBWeT0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "1073868858509-eumumh6fhdkcc1spm465itqnepp7uc5c.apps.googleusercontent.com",
"client_type": 3
}
]
}
"project_info": {
"project_number": "60755663443",
"project_id": "address-verification-app",
"storage_bucket": "address-verification-app.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:60755663443:android:988149d3da3c00d38584a6",
"android_client_info": {
"package_name": "com.avapp"
}
},
"oauth_client": [
{
"client_id": "60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyA70_d2M2ke-Mu0OHGZ6iZilBbD6A-_z0c"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
],
"configuration_version": "1"
}
}
],
"configuration_version": "1"
}

View File

@@ -16,7 +16,7 @@
"release:qa": "yarn move:qa && react-native run-android --variant=release && cd android && ./gradlew assembleRelease",
"release:prod": "yarn move:prod && react-native run-android --variant=release && cd android && ./gradlew assembleRelease",
"move:dev": "cp -f ./config/dev/config.js ./src/constants && cp -f ./config/dev/google-services.json ./android/app",
"move:qa": "cp -f ./config/qa/config.js ./src/constants && cp -f ./config/dev/google-services.json ./android/app",
"move:qa": "cp -f ./config/qa/config.js ./src/constants && cp -f ./config/qa/google-services.json ./android/app",
"move:prod": "cp -f ./config/prod/config.js ./src/constants && cp -f ./config/prod/google-services.json ./android/app",
"debug": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res",
"prepare": "husky install"
@@ -35,6 +35,7 @@
"@react-native-firebase/database": "16.4.6",
"@react-native-firebase/firestore": "16.5.0",
"@react-native-firebase/messaging": "17.4.0",
"@react-native-google-signin/google-signin": "9.0.2",
"@react-navigation/bottom-tabs": "6.5.5",
"@react-navigation/native": "6.1.4",
"@react-navigation/native-stack": "6.9.4",
@@ -45,7 +46,6 @@
"appcenter-analytics": "^4.4.5",
"appcenter-crashes": "^4.4.5",
"axios": "1.2.1",
"crypto-js": "^4.1.1",
"fuzzysort": "2.0.4",
"lottie-react-native": "5.1.4",
"react": "18.1.0",

View File

@@ -1,6 +1,5 @@
import { ToastMessages } from './../screens/allCases/constants';
import 'react-native-get-random-values';
import crypto from 'crypto-js';
import { IUser, setAuthData } from '../reducer/userSlice';
import axiosInstance, { ApiKeys, API_STATUS_CODE, getApiUrl } from '../components/utlis/apiHelper';
import {
@@ -21,8 +20,9 @@ import { toast } from '../../RN-UI-LIB/src/components/toast';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { clearAllAsyncStorage, setAsyncStorageItem } from '../components/utlis/commonFunctions';
import { logError } from '../components/utlis/errorUtils';
import { Linking } from 'react-native';
import auth from '@react-native-firebase/auth';
import { GenericType } from '../common/GenericTypes';
import { GoogleSignin } from '@react-native-google-signin/google-signin';
export interface GenerateOTPPayload {
phoneNumber: string;
@@ -33,10 +33,6 @@ export interface VerifyOTPPayload {
otpToken: string;
}
export interface SignInGooglePayload {
codeChallenge: string;
}
export const generateOTP =
({ phoneNumber }: GenerateOTPPayload, isResendOTP?: boolean) =>
(dispatch: Dispatch) => {
@@ -71,80 +67,46 @@ export const generateOTP =
.finally(() => dispatch(setFormLoading(false)));
};
export const signInGoogle = () => async (dispatch: Dispatch) => {
export const verifyGoogleSignIn = (idToken: string) => async (dispatch: AppDispatch) => {
const url = getApiUrl(ApiKeys.VERIFY_GOOGLE_SIGN_IN);
const fcmToken = await AsyncStorage.getItem('fcmtoken');
try {
const codeVerifier = crypto.lib.WordArray.random(64).toString();
const codeChallenge = crypto.enc.Base64url.stringify(
crypto.enc.Utf8.parse(crypto.SHA256(codeVerifier))
).toString();
await setAsyncStorageItem('codeVerifier', codeVerifier);
const state = crypto.lib.WordArray.random(16).toString();
const url = getApiUrl(ApiKeys.SIGN_IN_GOOGLE);
const response = await axiosInstance.get(url, {
params: {
codeChallenge,
state,
},
headers: { donotHandleError: false },
const response = await axiosInstance.post(url, {
idToken,
fcmToken,
deviceId: GLOBAL.DEVICE_ID,
deviceType: GLOBAL.DEVICE_TYPE,
});
if (response?.data?.signInUrl) {
await Linking.openURL(response.data.signInUrl);
if (response?.data?.sessionDetails) {
const { sessionDetails, user } = response.data;
dispatch(
setAuthData({
sessionDetails,
user,
isLoggedIn: true,
})
);
dispatch(setVerifyOTPSuccess('Login Successfully!'));
dispatch(resetLoginForm());
}
} catch (error) {
} catch (error: GenericType) {
await handleGoogleLogout();
logError(error as Error);
toast({
text1: 'Error in google sign in',
type: 'error',
});
if (error?.response?.status === API_STATUS_CODE.NOT_FOUND) {
toast({
text1: ToastMessages.GENERIC_ERROR_TOAST,
type: 'error',
});
} else {
toast({
text1: error?.response?.data?.message ?? ToastMessages.SSO_SERVER_SIGN_IN_ERROR,
type: 'error',
});
}
}
};
export const verifyGoogleSignIn =
(code: string, state: string, deviceId: string) => async (dispatch: AppDispatch) => {
const url = getApiUrl(ApiKeys.VERIFY_GOOGLE_SIGN_IN);
try {
const codeVerify = await AsyncStorage.getItem('codeVerifier');
const fcmToken = await AsyncStorage.getItem('fcmtoken');
if (!codeVerify) {
return;
}
const parsedCodeVerify = JSON.parse(codeVerify);
const response = await axiosInstance.post(url, {
code,
state,
codeVerifier: parsedCodeVerify,
deviceId: deviceId,
deviceType: GLOBAL.DEVICE_TYPE,
fcmToken,
});
if (response?.data?.sessionDetails) {
const { sessionDetails, user } = response.data;
dispatch(
setAuthData({
sessionDetails,
user,
isLoggedIn: true,
})
);
dispatch(setVerifyOTPSuccess('OTP verified'));
dispatch(resetLoginForm());
}
} catch (error) {
logError(error as Error);
if (error?.response?.status === API_STATUS_CODE.NOT_FOUND) {
toast({
text1: ToastMessages.GENERIC_ERROR_TOAST,
type: 'error',
});
} else {
toast({
text1: error?.response?.data?.message ?? ToastMessages.SSO_SERVER_SIGN_IN_ERROR,
type: 'error',
});
}
}
};
export const verifyOTP =
({ otp, otpToken }: VerifyOTPPayload) =>
async (dispatch: AppDispatch) => {
@@ -189,9 +151,17 @@ export const logout = () => (dispatch: AppDispatch) => {
});
};
export const handleGoogleLogout = async () => {
const currentUser = await GoogleSignin.getCurrentUser();
if (currentUser) {
await GoogleSignin.signOut();
}
};
export const handleLogout = () => async (dispatch: AppDispatch) => {
try {
await auth().signOut();
await handleGoogleLogout();
await clearAllAsyncStorage();
setGlobalUserData({ token: '', agentId: '', deviceId: '' });
dispatch(

View File

@@ -31,7 +31,6 @@ export enum ApiKeys {
NOTIFICATION_ACTION = 'NOTIFICATION_ACTION',
NOTIFICATION_DELIVERED = 'NOTIFICATION_DELIVERED',
SEND_LOCATION = 'SEND_LOCATION',
SIGN_IN_GOOGLE = 'SIGN_IN_GOOGLE',
VERIFY_GOOGLE_SIGN_IN = 'VERIFY_GOOGLE_SIGN_IN',
SYNC_TIME = 'SYNC_TIME',
IS_DATA_SYNC_REQUIRED = 'IS_DATA_SYNC_REQUIRED',
@@ -59,8 +58,7 @@ API_URLS[ApiKeys.NOTIFICATIONS] = '/notification/fetch';
API_URLS[ApiKeys.NOTIFICATION_ACTION] = '/notification/action';
API_URLS[ApiKeys.NOTIFICATION_DELIVERED] = '/notification/delivered';
API_URLS[ApiKeys.SEND_LOCATION] = '/geolocations/agents';
API_URLS[ApiKeys.SIGN_IN_GOOGLE] = '/auth/google/sign-in/url';
API_URLS[ApiKeys.VERIFY_GOOGLE_SIGN_IN] = '/auth/session/exchange';
API_URLS[ApiKeys.VERIFY_GOOGLE_SIGN_IN] = '/auth/session/internal/exchange';
API_URLS[ApiKeys.SYNC_TIME] = '/sync/server-timestamp';
API_URLS[ApiKeys.IS_DATA_SYNC_REQUIRED] = '/sync-data/is-sync-required';
API_URLS[ApiKeys.GET_PRE_SIGNED_URL_DATA_SYNC] = '/sync-data/get-pre-signed-url';

View File

@@ -5,8 +5,10 @@ export const SENTRY_DSN =
'https://acef93c884c1424cacc4ec899562e203@qa-longhorn-portal.np.navi-tech.in/glitchtip-events/173';
export const JANUS_SERVICE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/api/events/json';
export const ENV = 'qa';
export const IS_SSO_ENABLED = false;
export const IS_SSO_ENABLED = true;
export const APM_APP_NAME = 'cosmos-app';
export const APM_BASE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/apm-events';
export const IS_DATA_SYNC_REQUIRED = true;
export const DATA_SYNC_TIME_INTERVAL = 2 * MINUTES_IN_AN_HOUR * MILLISECONDS_IN_A_MINUTE; // 2hr
export const GOOGLE_SSO_CLIENT_ID =
'60755663443-40k0fbrbbqv4ci4hrjlbrphab5fj387b.apps.googleusercontent.com';

View File

@@ -1,8 +1,6 @@
import { Linking, StyleSheet } from 'react-native';
import React, { useEffect } from 'react';
import { getParamsObject } from '../../components/utlis/commonFunctions';
import { useAppDispatch } from '../../hooks';
import { verifyGoogleSignIn } from '../../action/authActions';
import { useSelector } from 'react-redux';
import { RootState } from '../../store/store';
import { getUniqueId, isTablet } from 'react-native-device-info';
@@ -23,19 +21,6 @@ const AuthRouter = () => {
const { isLoggedIn, deviceId, sessionDetails } = user;
useNativeButtons();
// Google signin url handler
const handleUrl = (url: string) => {
if (url) {
const decodedUri = decodeURIComponent(url);
const fragmentIndex = decodedUri.indexOf('#');
if (fragmentIndex !== -1) {
const params = decodedUri.substring(fragmentIndex + 1);
const { code, state } = getParamsObject(params);
dispatch(verifyGoogleSignIn(code, state, deviceId));
}
}
};
// Sets deviceId
useEffect(() => {
if (!deviceId) {

View File

@@ -9,20 +9,32 @@ import Text from '../../../RN-UI-LIB/src/components/Text';
import TextInput from '../../../RN-UI-LIB/src/components/TextInput';
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
import NaviLogoIcon from '../../../RN-UI-LIB/src/Icons/NaviLogoIcon';
import { generateOTP, type GenerateOTPPayload, signInGoogle } from '../../action/authActions';
import {
generateOTP,
verifyGoogleSignIn,
type GenerateOTPPayload,
handleGoogleLogout,
} from '../../action/authActions';
import { useAppDispatch } from '../../hooks';
import { type RootState } from '../../store/store';
import { addClickstreamEvent } from '../../services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
import Layout from '../layout/Layout';
import { IS_SSO_ENABLED } from '../../constants/config';
import { GOOGLE_SSO_CLIENT_ID, IS_SSO_ENABLED } from '../../constants/config';
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
import { GoogleSignin, User as GoogleSigninUser } from '@react-native-google-signin/google-signin';
import { GenericType } from '../../common/GenericTypes';
import { logError } from '../../components/utlis/errorUtils';
import GoogleIcon from '../../assets/icons/GoogleIcon';
interface ILoginForm {
phoneNumber: string;
}
GoogleSignin.configure({
webClientId: GOOGLE_SSO_CLIENT_ID,
});
function Login() {
const {
handleSubmit,
@@ -54,8 +66,19 @@ function Login() {
dispatch(generateOTP(data));
};
const signInWithGoogle = () => {
dispatch(signInGoogle());
const onPressGoogle = async () => {
try {
await GoogleSignin.hasPlayServices();
const userInfo: GoogleSigninUser = await GoogleSignin.signIn();
if (userInfo?.idToken) {
await dispatch(verifyGoogleSignIn(userInfo.idToken));
return;
}
throw userInfo;
} catch (error: GenericType) {
await handleGoogleLogout();
logError(error);
}
};
return (
@@ -105,7 +128,7 @@ function Login() {
testID="test_get_otp"
textStyle={GenericStyles.fontSize16}
/>
{IS_SSO_ENABLED ? (
{IS_SSO_ENABLED && GOOGLE_SSO_CLIENT_ID ? (
<>
<View style={[GenericStyles.centerAlignedRow, GenericStyles.mt24]}>
<View style={styles.horizontalLine} />
@@ -118,11 +141,10 @@ function Login() {
</View>
<Button
title="Log in with Google"
onPress={signInWithGoogle}
onPress={onPressGoogle}
style={[GenericStyles.w100, GenericStyles.mt24]}
variant="secondary"
buttonStyle={styles.googleButton}
disabled={isLoading}
testID="test_sso_login"
textStyle={[
{