upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/55.md
diff options
context:
space:
mode:
authorKieran <kieran@harkin.me>2024-10-15 11:15:54 +0100
committerGitHub <noreply@github.com>2024-10-15 11:15:54 +0100
commit1e2f19863ca56754daa2466881eb22087a71b17d (patch)
tree4f7be759dce290fea1d3ae10c403260b466ee16c /55.md
parent53afaaece61f02e92b5ef9c3e9c32945c7ebf522 (diff)
parente381b577c997b849fa544eea7dc9f08b360b4a33 (diff)
Merge branch 'master' into nip71-imeta
Diffstat (limited to '55.md')
-rw-r--r--55.md594
1 files changed, 594 insertions, 0 deletions
diff --git a/55.md b/55.md
new file mode 100644
index 0000000..d78bc8c
--- /dev/null
+++ b/55.md
@@ -0,0 +1,594 @@
1NIP-55
2======
3
4Android Signer Application
5--------------------------
6
7`draft` `optional`
8
9This NIP describes a method for 2-way communication between an Android signer and any Nostr client on Android. The Android signer is an Android Application and the client can be a web client or an Android application.
10
11# Usage for Android applications
12
13The Android signer uses Intents and Content Resolvers to communicate between applications.
14
15To be able to use the Android signer in your application you should add this to your AndroidManifest.xml:
16
17```xml
18<queries>
19 <intent>
20 <action android:name="android.intent.action.VIEW" />
21 <category android:name="android.intent.category.BROWSABLE" />
22 <data android:scheme="nostrsigner" />
23 </intent>
24</queries>
25```
26
27Then you can use this function to check if there's a signer application installed:
28
29```kotlin
30fun isExternalSignerInstalled(context: Context): Boolean {
31 val intent =
32 Intent().apply {
33 action = Intent.ACTION_VIEW
34 data = Uri.parse("nostrsigner:")
35 }
36 val infos = context.packageManager.queryIntentActivities(intent, 0)
37 return infos.size > 0
38}
39```
40
41## Using Intents
42
43To get the result back from the Signer Application you should use `registerForActivityResult` or `rememberLauncherForActivityResult` in Kotlin. If you are using another framework check the documentation of your framework or a third party library to get the result.
44
45```kotlin
46val launcher = rememberLauncherForActivityResult(
47 contract = ActivityResultContracts.StartActivityForResult(),
48 onResult = { result ->
49 if (result.resultCode != Activity.RESULT_OK) {
50 Toast.makeText(
51 context,
52 "Sign request rejected",
53 Toast.LENGTH_SHORT
54 ).show()
55 } else {
56 val signature = activityResult.data?.getStringExtra("signature")
57 // Do something with signature ...
58 }
59 }
60)
61```
62
63Create the Intent using the **nostrsigner** scheme:
64
65```kotlin
66val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$content"))
67```
68
69Set the Signer package name:
70
71```kotlin
72intent.`package` = "com.example.signer"
73```
74
75Send the Intent:
76
77```kotlin
78launcher.launch(intent)
79```
80
81### Methods
82
83- **get_public_key**
84 - params:
85
86 ```kotlin
87 val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:"))
88 intent.`package` = "com.example.signer"
89 intent.putExtra("type", "get_public_key")
90 // You can send some default permissions for the user to authorize for ever
91 val permissions = listOf(
92 Permission(
93 type = "sign_event",
94 kind = 22242
95 ),
96 Permission(
97 type = "nip44_decrypt"
98 )
99 )
100 intent.putExtra("permissions", permissions.toJson())
101 context.startActivity(intent)
102 ```
103 - result:
104 - If the user approved intent it will return the **pubkey** in the signature field
105
106 ```kotlin
107 val pubkey = intent.data?.getStringExtra("signature")
108 // The package name of the signer application
109 val packageName = intent.data?.getStringExtra("package")
110 ```
111
112- **sign_event**
113 - params:
114
115 ```kotlin
116 val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$eventJson"))
117 intent.`package` = "com.example.signer"
118 intent.putExtra("type", "sign_event")
119 // To handle results when not waiting between intents
120 intent.putExtra("id", event.id)
121 // Send the current logged in user pubkey
122 intent.putExtra("current_user", pubkey)
123
124 context.startActivity(intent)
125 ```
126 - result:
127 - If the user approved intent it will return the **signature**, **id** and **event** fields
128
129 ```kotlin
130 val signature = intent.data?.getStringExtra("signature")
131 // The id you sent
132 val id = intent.data?.getStringExtra("id")
133 val signedEventJson = intent.data?.getStringExtra("event")
134 ```
135
136- **nip04_encrypt**
137 - params:
138
139 ```kotlin
140 val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$plaintext"))
141 intent.`package` = "com.example.signer"
142 intent.putExtra("type", "nip04_encrypt")
143 // to control the result in your application in case you are not waiting the result before sending another intent
144 intent.putExtra("id", "some_id")
145 // Send the current logged in user pubkey
146 intent.putExtra("current_user", account.keyPair.pubkey)
147 // Send the hex pubkey that will be used for encrypting the data
148 intent.putExtra("pubkey", pubkey)
149
150 context.startActivity(intent)
151 ```
152 - result:
153 - If the user approved intent it will return the **signature** and **id** fields
154
155 ```kotlin
156 val encryptedText = intent.data?.getStringExtra("signature")
157 // the id you sent
158 val id = intent.data?.getStringExtra("id")
159 ```
160
161- **nip44_encrypt**
162 - params:
163
164 ```kotlin
165 val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$plaintext"))
166 intent.`package` = "com.example.signer"
167 intent.putExtra("type", "nip44_encrypt")
168 // to control the result in your application in case you are not waiting the result before sending another intent
169 intent.putExtra("id", "some_id")
170 // Send the current logged in user pubkey
171 intent.putExtra("current_user", account.keyPair.pubkey)
172 // Send the hex pubkey that will be used for encrypting the data
173 intent.putExtra("pubkey", pubkey)
174
175 context.startActivity(intent)
176 ```
177 - result:
178 - If the user approved intent it will return the **signature** and **id** fields
179
180 ```kotlin
181 val encryptedText = intent.data?.getStringExtra("signature")
182 // the id you sent
183 val id = intent.data?.getStringExtra("id")
184 ```
185
186- **nip04_decrypt**
187 - params:
188
189 ```kotlin
190 val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$encryptedText"))
191 intent.`package` = "com.example.signer"
192 intent.putExtra("type", "nip04_decrypt")
193 // to control the result in your application in case you are not waiting the result before sending another intent
194 intent.putExtra("id", "some_id")
195 // Send the current logged in user pubkey
196 intent.putExtra("current_user", account.keyPair.pubkey)
197 // Send the hex pubkey that will be used for decrypting the data
198 intent.putExtra("pubkey", pubkey)
199
200 context.startActivity(intent)
201 ```
202 - result:
203 - If the user approved intent it will return the **signature** and **id** fields
204
205 ```kotlin
206 val plainText = intent.data?.getStringExtra("signature")
207 // the id you sent
208 val id = intent.data?.getStringExtra("id")
209 ```
210
211- **nip44_decrypt**
212 - params:
213
214 ```kotlin
215 val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$encryptedText"))
216 intent.`package` = "com.example.signer"
217 intent.putExtra("type", "nip04_decrypt")
218 // to control the result in your application in case you are not waiting the result before sending another intent
219 intent.putExtra("id", "some_id")
220 // Send the current logged in user pubkey
221 intent.putExtra("current_user", account.keyPair.pubkey)
222 // Send the hex pubkey that will be used for decrypting the data
223 intent.putExtra("pubkey", pubkey)
224
225 context.startActivity(intent)
226 ```
227 - result:
228 - If the user approved intent it will return the **signature** and **id** fields
229
230 ```kotlin
231 val plainText = intent.data?.getStringExtra("signature")
232 // the id you sent
233 val id = intent.data?.getStringExtra("id")
234 ```
235
236- **get_relays**
237 - params:
238
239 ```kotlin
240 val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:"))
241 intent.`package` = "com.example.signer"
242 intent.putExtra("type", "get_relays")
243 // to control the result in your application in case you are not waiting the result before sending another intent
244 intent.putExtra("id", "some_id")
245 // Send the current logged in user pubkey
246 intent.putExtra("current_user", account.keyPair.pubkey)
247
248 context.startActivity(intent)
249 ```
250 - result:
251 - If the user approved intent it will return the **signature** and **id** fields
252
253 ```kotlin
254 val relayJsonText = intent.data?.getStringExtra("signature")
255 // the id you sent
256 val id = intent.data?.getStringExtra("id")
257 ```
258
259- **decrypt_zap_event**
260 - params:
261
262 ```kotlin
263 val intent = Intent(Intent.ACTION_VIEW, Uri.parse("nostrsigner:$eventJson"))
264 intent.`package` = "com.example.signer"
265 intent.putExtra("type", "decrypt_zap_event")
266 // to control the result in your application in case you are not waiting the result before sending another intent
267 intent.putExtra("id", "some_id")
268 // Send the current logged in user pubkey
269 intent.putExtra("current_user", account.keyPair.pubkey)
270 context.startActivity(intent)
271 ```
272 - result:
273 - If the user approved intent it will return the **signature** and **id** fields
274
275 ```kotlin
276 val eventJson = intent.data?.getStringExtra("signature")
277 // the id you sent
278 val id = intent.data?.getStringExtra("id")
279 ```
280
281## Using Content Resolver
282
283To get the result back from Signer Application you should use contentResolver.query in Kotlin. If you are using another framework check the documentation of your framework or a third party library to get the result.
284
285If the user did not check the "remember my choice" option, the pubkey is not in Signer Application or the signer type is not recognized the `contentResolver` will return null
286
287For the SIGN_EVENT type Signer Application returns two columns "signature" and "event". The column event is the signed event json
288
289For the other types Signer Application returns the column "signature"
290
291If the user chose to always reject the event, signer application will return the column "rejected" and you should not open signer application
292
293### Methods
294
295- **get_public_key**
296 - params:
297
298 ```kotlin
299 val result = context.contentResolver.query(
300 Uri.parse("content://com.example.signer.GET_PUBLIC_KEY"),
301 listOf("login"),
302 null,
303 null,
304 null
305 )
306 ```
307 - result:
308 - Will return the **pubkey** in the signature column
309
310 ```kotlin
311 if (result == null) return
312
313 if (result.moveToFirst()) {
314 val index = it.getColumnIndex("signature")
315 if (index < 0) return
316 val pubkey = it.getString(index)
317 }
318 ```
319
320- **sign_event**
321 - params:
322
323 ```kotlin
324 val result = context.contentResolver.query(
325 Uri.parse("content://com.example.signer.SIGN_EVENT"),
326 listOf("$eventJson", "", "${logged_in_user_pubkey}"),
327 null,
328 null,
329 null
330 )
331 ```
332 - result:
333 - Will return the **signature** and the **event** columns
334
335 ```kotlin
336 if (result == null) return
337
338 if (result.moveToFirst()) {
339 val index = it.getColumnIndex("signature")
340 val indexJson = it.getColumnIndex("event")
341 val signature = it.getString(index)
342 val eventJson = it.getString(indexJson)
343 }
344 ```
345
346- **nip04_encrypt**
347 - params:
348
349 ```kotlin
350 val result = context.contentResolver.query(
351 Uri.parse("content://com.example.signer.NIP04_ENCRYPT"),
352 listOf("$plainText", "${hex_pub_key}", "${logged_in_user_pubkey}"),
353 null,
354 null,
355 null
356 )
357 ```
358 - result:
359 - Will return the **signature** column
360
361 ```kotlin
362 if (result == null) return
363
364 if (result.moveToFirst()) {
365 val index = it.getColumnIndex("signature")
366 val encryptedText = it.getString(index)
367 }
368 ```
369
370- **nip44_encrypt**
371 - params:
372
373 ```kotlin
374 val result = context.contentResolver.query(
375 Uri.parse("content://com.example.signer.NIP44_ENCRYPT"),
376 listOf("$plainText", "${hex_pub_key}", "${logged_in_user_pubkey}"),
377 null,
378 null,
379 null
380 )
381 ```
382 - result:
383 - Will return the **signature** column
384
385 ```kotlin
386 if (result == null) return
387
388 if (result.moveToFirst()) {
389 val index = it.getColumnIndex("signature")
390 val encryptedText = it.getString(index)
391 }
392 ```
393
394- **nip04_decrypt**
395 - params:
396
397 ```kotlin
398 val result = context.contentResolver.query(
399 Uri.parse("content://com.example.signer.NIP04_DECRYPT"),
400 listOf("$encryptedText", "${hex_pub_key}", "${logged_in_user_pubkey}"),
401 null,
402 null,
403 null
404 )
405 ```
406 - result:
407 - Will return the **signature** column
408
409 ```kotlin
410 if (result == null) return
411
412 if (result.moveToFirst()) {
413 val index = it.getColumnIndex("signature")
414 val encryptedText = it.getString(index)
415 }
416 ```
417
418- **nip44_decrypt**
419 - params:
420
421 ```kotlin
422 val result = context.contentResolver.query(
423 Uri.parse("content://com.example.signer.NIP44_DECRYPT"),
424 listOf("$encryptedText", "${hex_pub_key}", "${logged_in_user_pubkey}"),
425 null,
426 null,
427 null
428 )
429 ```
430 - result:
431 - Will return the **signature** column
432
433 ```kotlin
434 if (result == null) return
435
436 if (result.moveToFirst()) {
437 val index = it.getColumnIndex("signature")
438 val encryptedText = it.getString(index)
439 }
440 ```
441
442- **get_relays**
443 - params:
444
445 ```kotlin
446 val result = context.contentResolver.query(
447 Uri.parse("content://com.example.signer.GET_RELAYS"),
448 listOf("${logged_in_user_pubkey}"),
449 null,
450 null,
451 null
452 )
453 ```
454 - result:
455 - Will return the **signature** column
456
457 ```kotlin
458 if (result == null) return
459
460 if (result.moveToFirst()) {
461 val index = it.getColumnIndex("signature")
462 val relayJsonText = it.getString(index)
463 }
464 ```
465
466- **decrypt_zap_event**
467 - params:
468
469 ```kotlin
470 val result = context.contentResolver.query(
471 Uri.parse("content://com.example.signer.DECRYPT_ZAP_EVENT"),
472 listOf("$eventJson", "", "${logged_in_user_pubkey}"),
473 null,
474 null,
475 null
476 )
477 ```
478 - result:
479 - Will return the **signature** column
480
481 ```kotlin
482 if (result == null) return
483
484 if (result.moveToFirst()) {
485 val index = it.getColumnIndex("signature")
486 val eventJson = it.getString(index)
487 }
488 ```
489
490# Usage for Web Applications
491
492Since web applications can't receive a result from the intent, you should add a modal to paste the signature or the event json or create a callback url.
493
494If you send the callback url parameter, Signer Application will send the result to the url.
495
496If you don't send a callback url, Signer Application will copy the result to the clipboard.
497
498You can configure the `returnType` to be **signature** or **event**.
499
500Android intents and browser urls have limitations, so if you are using the `returnType` of **event** consider using the parameter **compressionType=gzip** that will return "Signer1" + Base64 gzip encoded event json
501
502## Methods
503
504- **get_public_key**
505 - params:
506
507 ```js
508 window.href = `nostrsigner:?compressionType=none&returnType=signature&type=get_public_key&callbackUrl=https://example.com/?event=`;
509 ```
510
511- **sign_event**
512 - params:
513
514 ```js
515 window.href = `nostrsigner:${eventJson}?compressionType=none&returnType=signature&type=sign_event&callbackUrl=https://example.com/?event=`;
516 ```
517
518- **nip04_encrypt**
519 - params:
520
521 ```js
522 window.href = `nostrsigner:${plainText}?pubkey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip04_encrypt&callbackUrl=https://example.com/?event=`;
523 ```
524
525- **nip44_encrypt**
526 - params:
527
528 ```js
529 window.href = `nostrsigner:${plainText}?pubkey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip44_encrypt&callbackUrl=https://example.com/?event=`;
530 ```
531
532- **nip04_decrypt**
533 - params:
534
535 ```js
536 window.href = `nostrsigner:${encryptedText}?pubkey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip04_decrypt&callbackUrl=https://example.com/?event=`;
537 ```
538
539- **nip44_decrypt**
540 - params:
541
542 ```js
543 window.href = `nostrsigner:${encryptedText}?pubkey=${hex_pub_key}&compressionType=none&returnType=signature&type=nip44_decrypt&callbackUrl=https://example.com/?event=`;
544 ```
545
546- **get_relays**
547 - params:
548
549 ```js
550 window.href = `nostrsigner:?compressionType=none&returnType=signature&type=get_relays&callbackUrl=https://example.com/?event=`;
551 ```
552
553- **decrypt_zap_event**
554 - params:
555
556 ```js
557 window.href = `nostrsigner:${eventJson}?compressionType=none&returnType=signature&type=decrypt_zap_event&callbackUrl=https://example.com/?event=`;
558 ```
559
560## Example
561
562```js
563<!DOCTYPE html>
564<html lang="en">
565<head>
566 <meta charset="UTF-8">
567 <meta name="viewport" content="width=device-width, initial-scale=1.0">
568 <title>Document</title>
569</head>
570<body>
571 <h1>Test</h1>
572
573 <script>
574 window.onload = function() {
575 var url = new URL(window.location.href);
576 var params = url.searchParams;
577 if (params) {
578 var param1 = params.get("event");
579 if (param1) alert(param1)
580 }
581 let json = {
582 kind: 1,
583 content: "test"
584 }
585 let encodedJson = encodeURIComponent(JSON.stringify(json))
586 var newAnchor = document.createElement("a");
587 newAnchor.href = `nostrsigner:${encodedJson}?compressionType=none&returnType=signature&type=sign_event&callbackUrl=https://example.com/?event=`;
588 newAnchor.textContent = "Open External Signer";
589 document.body.appendChild(newAnchor)
590 }
591 </script>
592</body>
593</html>
594```