@@ -16,21 +16,31 @@ export interface PublicKeyChallengeConfig {
1616}
1717
1818const SAFE_IDENTIFIER = / ^ [ a - z _ ] [ a - z 0 - 9 _ ] * $ / ;
19+ const SAFE_CRYPTO_NETWORK = / ^ [ a - z 0 - 9 _ - ] { 1 , 64 } $ / i;
1920
2021function validateIdentifier ( name : string , label : string ) : void {
2122 if ( ! SAFE_IDENTIFIER . test ( name ) ) {
2223 throw new Error ( `PublicKeySignature: invalid ${ label } "${ name } " — must match /^[a-z_][a-z0-9_]*$/` ) ;
2324 }
2425}
2526
27+ function validateCryptoNetwork ( name : string ) : void {
28+ if ( ! SAFE_CRYPTO_NETWORK . test ( name ) ) {
29+ throw new Error (
30+ 'PublicKeySignature: invalid crypto_network — must match /^[a-z0-9_-]{1,64}$/i' ,
31+ ) ;
32+ }
33+ }
34+
2635const MAX_PUBLIC_KEY_LENGTH = 256 ;
2736const MAX_MESSAGE_LENGTH = 4096 ;
2837const MAX_SIGNATURE_LENGTH = 1024 ;
38+ const ENABLE_SIGNATURE_VERIFICATION = process . env . ENABLE_SIGNATURE_VERIFICATION === 'true' ;
2939
3040export const PublicKeySignature = ( pubkey_challenge : PublicKeyChallengeConfig ) : GraphileConfig . Plugin => {
3141 const {
3242 schema,
33- crypto_network : _crypto_network ,
43+ crypto_network,
3444 sign_up_with_key,
3545 sign_in_request_challenge,
3646 sign_in_record_failure,
@@ -42,6 +52,7 @@ export const PublicKeySignature = (pubkey_challenge: PublicKeyChallengeConfig):
4252 validateIdentifier ( sign_in_request_challenge , 'sign_in_request_challenge' ) ;
4353 validateIdentifier ( sign_in_record_failure , 'sign_in_record_failure' ) ;
4454 validateIdentifier ( sign_in_with_challenge , 'sign_in_with_challenge' ) ;
55+ validateCryptoNetwork ( crypto_network ) ;
4556
4657 return extendSchema ( ( ) => ( {
4758 typeDefs : gql `
@@ -157,11 +168,8 @@ export const PublicKeySignature = (pubkey_challenge: PublicKeyChallengeConfig):
157168 } ) ;
158169 } ,
159170
160- // NOTE: Crypto verification is currently stubbed (always returns false).
161- // The verifyMessage call from @pyramation /crypto-keys needs to be
162- // re-implemented, e.g. using interchainJS, before this mutation can
163- // succeed. Until then, verifyMessageForSigning will always record a
164- // failure and throw BAD_SIGNIN.
171+ // NOTE: Verification remains behind a feature flag until crypto
172+ // verification is re-implemented.
165173 verifyMessageForSigning ( _$mutation : any , fieldArgs : any ) {
166174 const $input = fieldArgs . getRaw ( 'input' ) ;
167175 const $withPgClient = ( grafastContext ( ) as any ) . get ( 'withPgClient' ) ;
@@ -180,24 +188,13 @@ export const PublicKeySignature = (pubkey_challenge: PublicKeyChallengeConfig):
180188 throw new Error ( 'INVALID_SIGNATURE' ) ;
181189 }
182190
183- // TODO: Re-implement crypto verification (e.g. using interchainJS).
184- // const network = Networks[crypto_network];
185- // const result = verifyMessage(message, publicKey, signature, network);
186- const result = false ;
191+ if ( ! ENABLE_SIGNATURE_VERIFICATION ) {
192+ // Fail closed without mutating lockout counters while verification
193+ // is disabled.
194+ throw new Error ( 'FEATURE_DISABLED' ) ;
195+ }
187196
188197 return withPgClient ( null , async ( pgClient : any ) => {
189- if ( ! result ) {
190- // Record failure outside transaction — must persist for rate limiting/lockout
191- await pgQueryWithContext ( {
192- client : pgClient ,
193- context : { role : 'anonymous' } ,
194- query : `SELECT * FROM "${ schema } "."${ sign_in_record_failure } "($1)` ,
195- variables : [ publicKey ] ,
196- skipTransaction : true
197- } ) ;
198- throw new Error ( 'BAD_SIGNIN' ) ;
199- }
200-
201198 // Only the success path needs a transaction (multi-step)
202199 await pgClient . query ( 'BEGIN' ) ;
203200 try {
0 commit comments