Files
CBS-Mobile-App/src/modules/services/screens/ResetTpinScreen.tsx
T
Abin ToppoandClaude Opus 5.5 5fbd13ba12 Add TPIN reset via OTP, with server-clock countdowns and redacted API logs
- Reset TPIN: empty-body initiate -> OTP -> encrypted new TPIN via
  /tpin/reset/*, from a Services tile, search, and "Forgot your TPIN?"
  on the transfer's TPIN sheet (returns to the same transfer review)
- Handle every documented code: SESSION_INVALID signs out, TPIN_NOT_SET
  offers Create TPIN, CBS customer/account/org refusals stop the flow,
  outages retry from the start, expired or spent OTP/token restarts,
  policy VALIDATION_ERROR stays on the new-TPIN step
- Secrets live in screen state only and are dropped as each step ends
- Countdowns use the envelope timestamp, kept aside by the interceptor,
  so a wrong device clock cannot skew OTP or token expiry
- New TPINs refuse any three-digit run (1235, 5987), for create too
- Dev API logger redacts the session token, OTPs, PINs, encrypted
  payloads and one-time tokens
- Tests: request bodies, RSA-OAEP SHA-256 payload decrypted with Node,
  fresh nonce on key retry, server-clock deadlines, log redaction

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 18:41:03 +05:30

49 lines
1.7 KiB
TypeScript

import React from 'react';
import { StyleSheet } from 'react-native';
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import { ScreenShell } from '../../../components';
import { useAppSelector } from '../../../app/hooks';
import { FeatureKey } from '../../../constants/featureKeys';
import { useFeature } from '../../../features';
import type { ServicesStackParamList } from '../../../navigation/types';
import { TpinResetFlow } from '../../payments/components/TpinResetFlow';
type Props = Readonly<NativeStackScreenProps<ServicesStackParamList, 'ResetTpin'>>;
/**
* Resets a forgotten transaction PIN, from the service desk.
*
* The signed-in session stands in for the MPIN that creating a TPIN asks for;
* the bank re-verifies the customer and an OTP to the registered mobile is the
* second factor.
*/
export default function ResetTpinScreen({ navigation }: Props) {
const session = useAppSelector((state) => state.auth.session);
const lastProfile = useAppSelector((state) => state.auth.lastProfile);
// Create TPIN is offered on TPIN_NOT_SET only where its route exists.
const canCreateTpin = useFeature(FeatureKey.SERVICES_TPIN_CHANGE);
return (
<ScreenShell
title="Reset TPIN"
subtitle="Set a new PIN for authorising fund transfers"
contentContainerStyle={styles.container}
>
<TpinResetFlow
mobileNumber={session?.mobileNumber ?? lastProfile?.mobileNumber}
onSuccess={() => navigation.goBack()}
onExit={() => navigation.goBack()}
onCreateTpin={canCreateTpin ? () => navigation.replace('SetTpin') : undefined}
/>
</ScreenShell>
);
}
const styles = StyleSheet.create({
container: {
padding: 16,
flexGrow: 1,
},
});