cookie-based nip-98-like login.

This commit is contained in:
fiatjaf 2023-10-28 21:40:54 -03:00
parent 0506a198c1
commit f850a485cb
No known key found for this signature in database
GPG Key ID: BAD43C4BE5C1A3A1
4 changed files with 60 additions and 19 deletions

View File

@ -3,35 +3,44 @@ package main
import (
"context"
"net/http"
"strings"
"github.com/nbd-wtf/go-nostr/nip19"
"github.com/theplant/htmlgo"
)
// embed ui files
func inviteTreeHandler(w http.ResponseWriter, r *http.Request) {
content := inviteTreePageHTML(r.Context(), InviteTreePageParams{})
content := inviteTreePageHTML(r.Context(), InviteTreePageParams{
LoggedUser: getLoggedUser(r),
})
htmlgo.Fprint(w, baseHTML(content), r.Context())
}
func addToWhitelistHandler(w http.ResponseWriter, r *http.Request) {
loggedUser := getLoggedUser(r)
pubkey := r.PostFormValue("pubkey")
if err := addToWhitelist(r.Context(), pubkey, s.RelayPubkey); err != nil {
if pfx, value, err := nip19.Decode(pubkey); err == nil && pfx == "npub" {
pubkey = value.(string)
}
if err := addToWhitelist(r.Context(), pubkey, loggedUser); err != nil {
http.Error(w, "failed to add to whitelist: "+err.Error(), 500)
return
}
content := buildInviteTree(r.Context(), s.RelayPubkey)
content := buildInviteTree(r.Context(), s.RelayPubkey, loggedUser)
htmlgo.Fprint(w, content, r.Context())
}
func removeFromWhitelistHandler(w http.ResponseWriter, r *http.Request) {
loggedUser := getLoggedUser(r)
pubkey := r.PostFormValue("pubkey")
if err := removeFromWhitelist(r.Context(), pubkey); err != nil {
if err := removeFromWhitelist(r.Context(), pubkey, loggedUser); err != nil {
http.Error(w, "failed to remove from whitelist: "+err.Error(), 500)
return
}
content := buildInviteTree(r.Context(), s.RelayPubkey)
content := buildInviteTree(r.Context(), s.RelayPubkey, loggedUser)
htmlgo.Fprint(w, content, r.Context())
}

15
main.go
View File

@ -1,12 +1,15 @@
package main
import (
"encoding/json"
"net/http"
"net/url"
"os"
"github.com/fiatjaf/khatru"
"github.com/fiatjaf/khatru/plugins/storage/badgern"
"github.com/kelseyhightower/envconfig"
"github.com/nbd-wtf/go-nostr"
"github.com/rs/zerolog"
)
@ -67,3 +70,15 @@ func main() {
log.Fatal().Err(err).Msg("failed to serve")
}
}
func getLoggedUser(r *http.Request) string {
if cookie, _ := r.Cookie("nip98"); cookie != nil {
if evtj, err := url.QueryUnescape(cookie.Value); err == nil {
var evt nostr.Event
if err := json.Unmarshal([]byte(evtj), &evt); err == nil {
return evt.PubKey
}
}
}
return ""
}

View File

@ -6,7 +6,7 @@ import (
. "github.com/theplant/htmlgo"
)
const buttonClass = "rounded-md bg-white p-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
const buttonClass = "rounded-md text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300"
func baseHTML(inside HTMLComponent) HTMLComponent {
navItemClass := "text-gray-600 hover:bg-gray-200 rounded-md px-3 py-2 font-medium"
@ -29,6 +29,7 @@ func baseHTML(inside HTMLComponent) HTMLComponent {
A().Text("information").Href("/").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
A().Text("invite tree").Href("/users").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
A().Text("reports").Href("/reports").Class(navItemClass).Attr("hx-boost", "true", "hx-target", "main", "hx-select", "main"),
A().Text("login").Href("#").Class(navItemClass).Attr("_", "on click get window.nostr.signEvent({created_at: Math.round(Date.now()/1000), kind: 27235, tags: [['u', location.href]], content: ''}) then get JSON.stringify(it) then set cookies['nip98'] to it"),
).Class("flex flex-1 items-center justify-center"),
Main(inside).Class("m-4"),
).Class("mx-4 my-6"),
@ -68,29 +69,41 @@ func homePageHTML(ctx context.Context, params HomePageParams) HTMLComponent {
)
}
type InviteTreePageParams struct{}
type InviteTreePageParams struct {
LoggedUser string
}
func inviteTreePageHTML(ctx context.Context, params InviteTreePageParams) HTMLComponent {
return Form(
Input("pubkey").Type("text").Placeholder("npub1...").Class("w-96 rounded-md border-0 p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600"),
Button("invite").Class(buttonClass),
Button("invite").Class(buttonClass+" p-2 bg-white hover:bg-gray-50"),
Div(
buildInviteTree(ctx, s.RelayPubkey),
buildInviteTree(ctx, s.RelayPubkey, params.LoggedUser),
).Id("tree").Class("mt-3"),
).Attr("hx-post", "/add-to-whitelist", "hx-trigger", "submit", "hx-target", "#tree")
}
func buildInviteTree(ctx context.Context, invitedBy string) HTMLComponent {
func buildInviteTree(ctx context.Context, invitedBy string, loggedUser string) HTMLComponent {
children := make([]HTMLComponent, 0, len(whitelist))
for _, entry := range whitelist {
if entry.InvitedBy == invitedBy {
user := getUserInfo(ctx, entry.PublicKey)
button := Span("")
if invitedBy == loggedUser {
button = Button("remove").
Class(buttonClass+" px-2 bg-red-100 hover:bg-red-300").
Attr("hx-post", "/remove-from-whitelist",
"hx-trigger", "click",
"hx-target", "#tree",
"hx-vals", `{"pubkey": "`+entry.PublicKey+`"}`)
}
children = append(children,
Li(
A().Href("nostr:"+user.Npub).Text(user.Name).Class("font-mono"),
Button("remove").Class(buttonClass).Attr("hx-post", "/remove-from-whitelist", "hx-trigger", "click", "hx-target", "#tree"),
buildInviteTree(ctx, entry.PublicKey),
).Class("ml-3"),
A().Href("nostr:"+user.Npub).Text(user.Name).Class("font-mono py-1"),
button,
buildInviteTree(ctx, entry.PublicKey, loggedUser),
).Class("ml-4"),
)
}
}

View File

@ -3,6 +3,7 @@ package main
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/nbd-wtf/go-nostr"
@ -71,18 +72,21 @@ func whitelistRejecter(ctx context.Context, evt *nostr.Event) (reject bool, msg
return false, ""
}
func addToWhitelist(ctx context.Context, pubkey string, invitedBy string) error {
if nostr.IsValidPublicKeyHex(pubkey) && !isPublicKeyInWhitelist(pubkey) {
whitelist = append(whitelist, WhitelistEntry{PublicKey: pubkey, InvitedBy: invitedBy})
func addToWhitelist(ctx context.Context, pubkey string, inviter string) error {
if nostr.IsValidPublicKeyHex(pubkey) && isPublicKeyInWhitelist(inviter) && !isPublicKeyInWhitelist(pubkey) {
whitelist = append(whitelist, WhitelistEntry{PublicKey: pubkey, InvitedBy: inviter})
}
return saveWhitelist()
}
func removeFromWhitelist(ctx context.Context, pubkey string) error {
func removeFromWhitelist(ctx context.Context, pubkey string, deleter string) error {
idx := slices.IndexFunc(whitelist, func(we WhitelistEntry) bool { return we.PublicKey == pubkey })
if idx == -1 {
return nil
}
if whitelist[idx].InvitedBy != deleter {
return fmt.Errorf("can't remove a user you haven't invited")
}
whitelist = append(whitelist[0:idx], whitelist[idx+1:]...)
return saveWhitelist()