Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/src/providers/selfClientProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { TrackEventParams } from '@selfxyz/mobile-sdk-alpha';
import { selfClientDocumentsAdapter } from '@/providers/passportDataProvider';
import analytics from '@/utils/analytics';

import { unsafe_getPrivateKey } from './authProvider';

/**
* Provides a configured Self SDK client instance to all descendants.
*
Expand Down Expand Up @@ -82,6 +84,9 @@ export const SelfClientProvider = ({ children }: PropsWithChildren) => {
analytics().trackEvent(event, data);
},
},
auth: {
getPrivateKey: () => unsafe_getPrivateKey(),
},
}),
[],
);
Expand Down
23 changes: 18 additions & 5 deletions app/src/screens/dev/DevPrivateKeyScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { useCallback, useEffect, useState } from 'react';
import { Button, Text, XStack, YStack } from 'tamagui';
import Clipboard from '@react-native-clipboard/clipboard';

import { unsafe_getPrivateKey } from '@/providers/authProvider';
import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';

import { black, slate50, slate200, teal500, white } from '@/utils/colors';
import { confirmTap } from '@/utils/haptic';

Expand All @@ -16,12 +17,24 @@ const DevPrivateKeyScreen: React.FC = () => {
);
const [isPrivateKeyRevealed, setIsPrivateKeyRevealed] = useState(false);
const [copied, setCopied] = useState(false);
const selfClient = useSelfClient();

useEffect(() => {
unsafe_getPrivateKey().then(key =>
setPrivateKey(key || 'No private key found'),
);
}, []);
let mounted = true;
selfClient
.getPrivateKey()
.then(key => {
if (!mounted) return;
setPrivateKey(key || 'No private key found');
})
.catch(() => {
if (!mounted) return;
setPrivateKey('No private key found');
});
return () => {
mounted = false;
};
}, [selfClient]);

const handleRevealPrivateKey = useCallback(() => {
confirmTap();
Expand Down
5 changes: 1 addition & 4 deletions app/src/utils/proving/provingMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ import {
} from '@selfxyz/mobile-sdk-alpha/constants/analytics';

import { navigationRef } from '@/navigation';
// this will be pass as property of from selfClient
import { unsafe_getPrivateKey } from '@/providers/authProvider';
// will need to be passed in from selfClient
import {
clearPassportData,
Expand Down Expand Up @@ -651,8 +649,7 @@ export const useProvingStore = create<ProvingState>((set, get) => {

const { data: passportData } = selectedDocument;

// TODO call on self client
const secret = await unsafe_getPrivateKey();
const secret = await selfClient.getPrivateKey();
if (!secret) {
console.error('Could not load secret');
trackEvent(ProofEvents.LOAD_SECRET_FAILED);
Expand Down
2 changes: 0 additions & 2 deletions packages/mobile-sdk-alpha/src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ export type { SdkErrorCategory } from './errors';

export { SCANNER_ERROR_CODES, notImplemented, sdkError } from './errors';
export { SelfClientContext, SelfClientProvider, useSelfClient } from './context';
// Browser-only high-level component (DOM-based)
export { SelfMobileSdk as SelfMobileSdkHighLevel } from './components/SelfMobileSdk';

export { createSelfClient } from './client';

Expand Down
28 changes: 24 additions & 4 deletions packages/mobile-sdk-alpha/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { TrackEventParams } from './types/public';
* own. These defaults are intentionally minimal no-ops suitable for tests and
* non-production environments.
*/
const optionalDefaults: Partial<Adapters> = {
const optionalDefaults: Required<Pick<Adapters, 'storage' | 'clock' | 'logger'>> = {
storage: {
get: async () => null,
set: async () => {},
Expand All @@ -47,7 +47,7 @@ const optionalDefaults: Partial<Adapters> = {
},
};

const REQUIRED_ADAPTERS = ['scanner', 'network', 'crypto', 'documents'] as const;
const REQUIRED_ADAPTERS = ['auth', 'scanner', 'network', 'crypto', 'documents'] as const;

/**
* Creates a fully configured {@link SelfClient} instance.
Expand All @@ -56,14 +56,14 @@ const REQUIRED_ADAPTERS = ['scanner', 'network', 'crypto', 'documents'] as const
* provided configuration with sensible defaults. Missing optional adapters are
* filled with benign no-op implementations.
*/
export function createSelfClient({ config, adapters }: { config: Config; adapters: Partial<Adapters> }): SelfClient {
export function createSelfClient({ config, adapters }: { config: Config; adapters: Adapters }): SelfClient {
const cfg = mergeConfig(defaultConfig, config);

for (const name of REQUIRED_ADAPTERS) {
if (!(name in adapters) || !adapters[name as keyof Adapters]) throw notImplemented(name);
}

const _adapters = { ...optionalDefaults, ...adapters } as Adapters;
const _adapters = { ...optionalDefaults, ...adapters };
const listeners = new Map<SDKEvent, Set<(p: any) => void>>();

function on<E extends SDKEvent>(event: E, cb: (payload: SDKEventMap[E]) => void): Unsubscribe {
Expand Down Expand Up @@ -128,10 +128,30 @@ export function createSelfClient({ config, adapters }: { config: Config; adapter
return adapters.analytics.trackEvent(event, payload);
}

/**
* Retrieves the private key via the auth adapter.
* With great power comes great responsibility
*/
async function getPrivateKey(): Promise<string | null> {
return adapters.auth.getPrivateKey();
}
Comment on lines +131 to +137
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Gate getPrivateKey behind an explicit feature flag to reduce accidental exposure

Even in alpha, protect this API behind config.features.devExposePrivateKey === true.

   async function getPrivateKey(): Promise<string | null> {
-    return adapters.auth.getPrivateKey();
+    if (cfg.features?.devExposePrivateKey !== true) {
+      throw new Error('private-key access is disabled by configuration');
+    }
+    return adapters.auth.getPrivateKey();
   }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/mobile-sdk-alpha/src/client.ts around lines 131-137, gate the
getPrivateKey API behind the explicit feature flag
config.features.devExposePrivateKey === true: import or access the module
config, check the flag at the start of getPrivateKey, and only call and return
adapters.auth.getPrivateKey() when the flag is true; otherwise return null (or
reject/throw consistently with surrounding API behavior). Ensure you don't call
adapters.auth.getPrivateKey() when the flag is false and keep the function
signature Promise<string | null> intact.


async function hasPrivateKey(): Promise<boolean> {
if (!adapters.auth) return false;
try {
const key = await adapters.auth.getPrivateKey();
return !!key;
} catch {
return false;
}
}

return {
scanDocument,
validateDocument,
trackEvent,
getPrivateKey,
hasPrivateKey,
checkRegistration,
registerDocument,
generateProof,
Expand Down
73 changes: 0 additions & 73 deletions packages/mobile-sdk-alpha/src/components/SelfMobileSdk.tsx

This file was deleted.

63 changes: 0 additions & 63 deletions packages/mobile-sdk-alpha/src/components/flows/OnboardingFlow.tsx

This file was deleted.

4 changes: 2 additions & 2 deletions packages/mobile-sdk-alpha/src/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface SelfClientProviderProps {
* Partial set of adapter implementations. Any missing optional adapters will
* be replaced with default no-op implementations.
*/
adapters?: Partial<Adapters>;
adapters: Adapters;
}

export { SelfClientContext };
Expand All @@ -40,7 +40,7 @@ export { SelfClientContext };
* Consumers should ensure that `config` and `adapters` are referentially stable
* (e.g. wrapped in `useMemo`) to avoid recreating the client on every render.
*/
export function SelfClientProvider({ config, adapters = {}, children }: PropsWithChildren<SelfClientProviderProps>) {
export function SelfClientProvider({ config, adapters, children }: PropsWithChildren<SelfClientProviderProps>) {
const client = useMemo(() => createSelfClient({ config, adapters }), [config, adapters]);

return <SelfClientContext.Provider value={client}>{children}</SelfClientContext.Provider>;
Expand Down
4 changes: 2 additions & 2 deletions packages/mobile-sdk-alpha/src/entry/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import type { Adapters, Config } from '../types/public';

export interface SelfMobileSdkProps {
config: Config;
adapters?: Partial<Adapters>;
adapters: Adapters;
children?: ReactNode;
}

export const SelfMobileSdk = ({ config, adapters = {}, children }: SelfMobileSdkProps) => (
export const SelfMobileSdk = ({ config, adapters, children }: SelfMobileSdkProps) => (
<SelfClientProvider config={config} adapters={adapters}>
{children}
</SelfClientProvider>
Expand Down
38 changes: 0 additions & 38 deletions packages/mobile-sdk-alpha/src/hooks/useDocumentManager.ts

This file was deleted.

8 changes: 1 addition & 7 deletions packages/mobile-sdk-alpha/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export type {
// MRZ module
export type { DG1, DG2, NFCScanOptions, ParsedNFCResponse } from './nfc';

export type { DocumentData, DocumentMetadata, ExternalAdapter, PassportCameraProps, ScreenProps } from './types/ui';
export type { DocumentData, DocumentMetadata, PassportCameraProps, ScreenProps } from './types/ui';

export type { MRZScanOptions } from './mrz';

Expand All @@ -65,9 +65,6 @@ export {

export { NFCScannerScreen } from './components/screens/NFCScannerScreen';

// Flow Components
export { OnboardingFlow } from './components/flows/OnboardingFlow';

// Screen Components
export { PassportCameraScreen } from './components/screens/PassportCameraScreen';

Expand Down Expand Up @@ -100,8 +97,5 @@ export { parseNFCResponse, scanNFC } from './nfc';

export { scanQRProof } from './qr';

// Hooks
export { useDocumentManager } from './hooks/useDocumentManager';

// Error handling
export { webScannerShim } from './adapters/web/shims';
Loading
Loading