@@ -16,7 +16,7 @@ import {
1616 ActivityIndicator ,
1717 Keyboard ,
1818 StyleSheet ,
19- Linking , // <-- add Linking from react-native
19+ Linking ,
2020} from 'react-native' ;
2121import { Stack , router } from 'expo-router' ;
2222import { VStack } from 'components/ui/View/VStack' ;
@@ -30,6 +30,8 @@ import { useTheme } from 'providers/ThemeProvider';
3030import { withSheetProvider } from 'hocs/withSheetProvider' ;
3131import Icon from 'assets/icons' ;
3232import opacity from 'hex-color-opacity' ;
33+ import { useNostrKeysContext } from 'providers/NostrKeysProvider' ;
34+ import { finalizeEvent } from 'nostr-tools' ;
3335
3436// Available domains for Lightning addresses
3537const DOMAINS = [
@@ -215,8 +217,36 @@ function DomainOption({
215217 ) ;
216218}
217219
220+ /**
221+ * Generate NIP-98 HTTP Auth string for npub.cash API
222+ * @param url - The full URL being accessed
223+ * @param method - HTTP method (GET, POST, PUT, etc.)
224+ * @param privateKey - Nostr private key as Uint8Array
225+ * @returns Base64 encoded signed event with "Nostr " prefix
226+ */
227+ function generateNip98Auth ( url : string , method : string , privateKey : Uint8Array ) : string {
228+ // Create the NIP-98 event structure
229+ const authEvent = {
230+ content : '' ,
231+ kind : 27235 ,
232+ created_at : Math . floor ( Date . now ( ) / 1000 ) ,
233+ tags : [
234+ [ 'u' , url ] ,
235+ [ 'method' , method ] ,
236+ ] ,
237+ } ;
238+
239+ // Sign the event with the private key
240+ const signedEvent = finalizeEvent ( authEvent , privateKey ) ;
241+
242+ // Base64 encode the signed event and prefix with "Nostr "
243+ // btoa is available in React Native/Expo environments
244+ return `Nostr ${ btoa ( JSON . stringify ( signedEvent ) ) } ` ;
245+ }
246+
218247function ClaimUsernameScreen ( ) {
219248 const { getPrimaryColor } = useTheme ( ) ;
249+ const { keys : nostrKeys } = useNostrKeysContext ( ) ;
220250 const [ username , setUsername ] = useState ( '' ) ;
221251 const [ selectedDomain , setSelectedDomain ] = useState < DomainId > ( 'npubx' ) ;
222252 const [ availabilityResults , setAvailabilityResults ] = useState < AvailabilityResult [ ] > ( [ ] ) ;
@@ -303,12 +333,29 @@ function ClaimUsernameScreen() {
303333 return result ?. available === true ;
304334 } , [ availabilityResults , selectedDomain ] ) ;
305335
306- // Modified handleContinue: Open "https:// npub.cash/username" (replace username accordingly)
336+ // Generate NIP-98 auth for npub.cash and navigate to local server with auth
307337 const handleContinue = useCallback ( ( ) => {
308338 Keyboard . dismiss ( ) ;
309- // Fallback to just username without domain as per prompt
310- Linking . openURL ( `https://npub.cash/username` ) ;
311- } , [ ] ) ;
339+
340+ if ( ! nostrKeys ?. privateKey ) {
341+ console . error ( 'No Nostr private key available' ) ;
342+ return ;
343+ }
344+
345+ // The URL we're authenticating for (npub.cash API endpoint)
346+ const npubCashApiUrl = 'https://npub.cash/api/v1/info/username' ;
347+
348+ // Generate NIP-98 auth string for PUT request to npub.cash
349+ const nostrAuth = generateNip98Auth ( npubCashApiUrl , 'PUT' , nostrKeys . privateKey ) ;
350+
351+ // URL encode the auth string for use as query parameter
352+ const encodedAuth = encodeURIComponent ( nostrAuth ) ;
353+
354+ // Navigate to local server with nostr auth as query parameter
355+ const localUrl = `http://localhost:8080/api/npubcash-server/username?nostr:authorization=${ encodedAuth } ` ;
356+
357+ Linking . openURL ( localUrl ) ;
358+ } , [ nostrKeys ?. privateKey ] ) ;
312359
313360 const selectedDomainLabel = DOMAINS . find ( ( d ) => d . id === selectedDomain ) ?. value || '' ;
314361
0 commit comments