• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

agentic-dev-library / thumbcode / 21933729139

12 Feb 2026 04:36AM UTC coverage: 28.401% (+0.7%) from 27.702%
21933729139

Pull #116

github

web-flow
Merge b9d1b07d1 into c6c31bd07
Pull Request #116: refactor: decompose 9 monolith files into focused modules

406 of 2268 branches covered (17.9%)

Branch coverage included in aggregate %.

365 of 845 new or added lines in 22 files covered. (43.2%)

1 existing line in 1 file now uncovered.

1120 of 3105 relevant lines covered (36.07%)

7.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.46
/packages/core/src/credentials/KeyStorage.ts
1
/**
2
 * Key Storage
3
 *
4
 * Handles secure credential storage and retrieval using Expo SecureStore
5
 * with hardware-backed encryption. Includes biometric authentication support.
6
 */
7

8
import * as LocalAuthentication from 'expo-local-authentication';
9
import * as SecureStore from 'expo-secure-store';
10
import type {
11
  BiometricResult,
12
  CredentialType,
13
  RetrieveOptions,
14
  RetrieveResult,
15
  SecureCredential,
16
  StoreOptions,
17
  ValidationResult,
18
} from './types';
19
import { validate } from './validation';
20
import type { KeyValidator } from './KeyValidator';
21

22
// SecureStore key prefixes for different credential types
23
const SECURE_STORE_KEYS: Record<CredentialType, string> = {
5✔
24
  github: 'thumbcode_cred_github',
25
  anthropic: 'thumbcode_cred_anthropic',
26
  openai: 'thumbcode_cred_openai',
27
  mcp_server: 'thumbcode_cred_mcp',
28
  gitlab: 'thumbcode_cred_gitlab',
29
  bitbucket: 'thumbcode_cred_bitbucket',
30
  mcp_signing_secret: 'thumbcode_cred_mcp_signing_secret',
31
};
32

33
export class KeyStorage {
34
  constructor(private validator: KeyValidator) {}
35

36
  /**
37
   * Check if biometric authentication is available on the device
38
   */
39
  async isBiometricAvailable(): Promise<boolean> {
NEW
40
    const hasHardware = await LocalAuthentication.hasHardwareAsync();
×
NEW
41
    const isEnrolled = await LocalAuthentication.isEnrolledAsync();
×
NEW
42
    return hasHardware && isEnrolled;
×
43
  }
44

45
  /**
46
   * Get the available biometric authentication types
47
   */
48
  async getBiometricTypes(): Promise<LocalAuthentication.AuthenticationType[]> {
NEW
49
    return LocalAuthentication.supportedAuthenticationTypesAsync();
×
50
  }
51

52
  /**
53
   * Perform biometric authentication
54
   */
55
  async authenticateWithBiometrics(
56
    promptMessage = 'Authenticate to access your credentials'
×
57
  ): Promise<BiometricResult> {
NEW
58
    try {
×
NEW
59
      const result = await LocalAuthentication.authenticateAsync({
×
60
        promptMessage,
61
        cancelLabel: 'Cancel',
62
        disableDeviceFallback: false,
63
        fallbackLabel: 'Use passcode',
64
      });
65

NEW
66
      if (result.success) {
×
NEW
67
        return { success: true };
×
68
      }
NEW
69
      return {
×
70
        success: false,
71
        error: result.error,
72
      };
73
    } catch (error) {
NEW
74
      return {
×
75
        success: false,
76
        error: error instanceof Error ? error.message : 'Authentication failed',
×
77
      };
78
    }
79
  }
80

81
  /**
82
   * Store a credential securely
83
   */
84
  async store(
85
    type: CredentialType,
86
    secret: string,
87
    options: StoreOptions = {}
×
88
  ): Promise<ValidationResult> {
89
    const { requireBiometric = false, skipValidation = false } = options;
3✔
90

91
    if (!validate(type, secret)) {
3!
92
      return { isValid: false, message: 'Invalid credential format' };
3✔
93
    }
94

95
    // Biometric check if required
NEW
96
    if (requireBiometric) {
×
NEW
97
      const biometricResult = await this.authenticateWithBiometrics();
×
NEW
98
      if (!biometricResult.success) {
×
NEW
99
        return { isValid: false, message: 'Biometric authentication failed' };
×
100
      }
101
    }
102

103
    // Validate the credential before storing (unless skipped)
NEW
104
    if (!skipValidation) {
×
NEW
105
      const validation = await this.validator.validateCredential(type, secret);
×
NEW
106
      if (!validation.isValid) {
×
NEW
107
        return validation;
×
108
      }
109
    }
110

111
    // Store the secret in SecureStore
NEW
112
    const key = SECURE_STORE_KEYS[type];
×
NEW
113
    try {
×
NEW
114
      const payload: SecureCredential = {
×
115
        secret,
116
        storedAt: new Date().toISOString(),
117
        type,
118
      };
119

NEW
120
      await SecureStore.setItemAsync(key, JSON.stringify(payload), {
×
121
        keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
122
      });
123

NEW
124
      return { isValid: true, message: 'Credential stored successfully' };
×
125
    } catch (error) {
NEW
126
      return {
×
127
        isValid: false,
128
        message: error instanceof Error ? error.message : 'Failed to store credential',
×
129
      };
130
    }
131
  }
132

133
  /**
134
   * Retrieve a credential securely
135
   */
136
  async retrieve(type: CredentialType, options: RetrieveOptions = {}): Promise<RetrieveResult> {
3✔
137
    const { requireBiometric = false } = options;
3✔
138

139
    // Biometric check if required
140
    if (requireBiometric) {
3!
NEW
141
      const biometricResult = await this.authenticateWithBiometrics();
×
NEW
142
      if (!biometricResult.success) {
×
NEW
143
        return { secret: null };
×
144
      }
145
    }
146

147
    try {
3✔
148
      const key = SECURE_STORE_KEYS[type];
3✔
149
      const payload = await SecureStore.getItemAsync(key);
3✔
150

151
      if (!payload) {
3!
NEW
152
        return { secret: null };
×
153
      }
154

155
      const data: SecureCredential = JSON.parse(payload);
3✔
156

157
      return {
3✔
158
        secret: data.secret,
159
        metadata: {
160
          storedAt: data.storedAt,
161
          type: data.type,
162
        },
163
      };
164
    } catch (error) {
NEW
165
      console.error('Failed to retrieve credential:', error);
×
NEW
166
      return { secret: null };
×
167
    }
168
  }
169

170
  /**
171
   * Delete a credential securely
172
   */
173
  async delete(type: CredentialType): Promise<boolean> {
NEW
174
    try {
×
NEW
175
      const key = SECURE_STORE_KEYS[type];
×
NEW
176
      await SecureStore.deleteItemAsync(key);
×
NEW
177
      return true;
×
178
    } catch (error) {
NEW
179
      console.error('Failed to delete credential:', error);
×
NEW
180
      return false;
×
181
    }
182
  }
183

184
  /**
185
   * Check if a credential exists
186
   */
187
  async exists(type: CredentialType): Promise<boolean> {
188
    try {
7✔
189
      const key = SECURE_STORE_KEYS[type];
7✔
190
      const value = await SecureStore.getItemAsync(key);
7✔
191
      return value !== null;
7✔
192
    } catch {
NEW
193
      return false;
×
194
    }
195
  }
196

197
  /**
198
   * Get all stored credential types
199
   */
200
  async getStoredCredentialTypes(): Promise<CredentialType[]> {
201
    const types = Object.keys(SECURE_STORE_KEYS) as CredentialType[];
1✔
202
    const results = await Promise.all(types.map((type) => this.exists(type)));
7✔
203
    return types.filter((_, index) => results[index]);
7✔
204
  }
205

206
  /**
207
   * Validate all stored credentials and return results
208
   */
209
  async validateAllStored(): Promise<Map<CredentialType, ValidationResult>> {
210
    const results = new Map<CredentialType, ValidationResult>();
1✔
211
    const storedTypes = await this.getStoredCredentialTypes();
1✔
212

213
    await Promise.all(
1✔
214
      storedTypes.map(async (type) => {
215
        const { secret } = await this.retrieve(type);
3✔
216
        if (secret) {
3!
217
          const result = await this.validator.validateCredential(type, secret);
3✔
218
          results.set(type, result);
3✔
219
        }
220
      })
221
    );
222

223
    return results;
1✔
224
  }
225
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc