BYOK Personal Data Server (PDS) written in Go
ipfs vow atproto pds go
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: prettify signing ops

+83 -25
+41
server/handle_proxy.go
··· 119 119 } 120 120 defer func() { _ = resp.Body.Close() }() 121 121 122 + // Strip hop-by-hop and CORS headers from upstream 123 + hopByHop := []string{ 124 + "Connection", 125 + "Keep-Alive", 126 + "Proxy-Authenticate", 127 + "Proxy-Authorization", 128 + "TE", 129 + "Trailers", 130 + "Transfer-Encoding", 131 + "Upgrade", 132 + "Content-Length", 133 + } 134 + corsHeaders := []string{ 135 + "Access-Control-Allow-Origin", 136 + "Access-Control-Allow-Methods", 137 + "Access-Control-Allow-Headers", 138 + "Access-Control-Expose-Headers", 139 + "Access-Control-Max-Age", 140 + "Access-Control-Allow-Credentials", 141 + } 142 + 122 143 for k, v := range resp.Header { 144 + skip := false 145 + for _, h := range hopByHop { 146 + if strings.EqualFold(k, h) { 147 + skip = true 148 + break 149 + } 150 + } 151 + if skip { 152 + continue 153 + } 154 + for _, h := range corsHeaders { 155 + if strings.EqualFold(k, h) { 156 + skip = true 157 + break 158 + } 159 + } 160 + if skip { 161 + continue 162 + } 123 163 w.Header().Set(k, strings.Join(v, ",")) 124 164 } 165 + 125 166 w.WriteHeader(resp.StatusCode) 126 167 if _, err := io.Copy(w, resp.Body); err != nil { 127 168 logger.Error("failed to copy response body", "error", err)
+1 -5
server/handle_server_get_service_auth.go
··· 131 131 132 132 // Request a signature from the user's browser via the WebSocket connection. 133 133 requestID := uuid.NewString() 134 - msg, err := json.Marshal(map[string]string{ 135 - "type": "sign_jwt_request", 136 - "requestId": requestID, 137 - "jwtPayload": base64.RawURLEncoding.EncodeToString([]byte(signingString)), 138 - }) 134 + msg, err := s.buildSignJWTRequestMsg(requestID, base64.RawURLEncoding.EncodeToString([]byte(signingString)), aud, lxm) 139 135 if err != nil { 140 136 return "", fmt.Errorf("marshalling sign_jwt_request: %w", err) 141 137 }
+20
server/handle_signer_connect.go
··· 43 43 ExpiresAt string `json:"expiresAt"` // RFC3339 44 44 } 45 45 46 + // wsSignJWTRequest is the JSON envelope pushed to the signer for service-auth 47 + // JWT signing (CompatMode). 48 + type wsSignJWTRequest struct { 49 + Type string `json:"type"` // always "sign_jwt_request" 50 + RequestID string `json:"requestId"` 51 + JWTPayload string `json:"jwtPayload"` // base64url-encoded header.payload 52 + Aud string `json:"aud"` // the audience (service DID) 53 + Lxm string `json:"lxm"` // the lexrpc method (optional) 54 + } 55 + 46 56 type wsIncoming struct { 47 57 Type string `json:"type"` 48 58 RequestID string `json:"requestId"` ··· 287 297 Payload: payloadB64, 288 298 Ops: ops, 289 299 ExpiresAt: expiresAt.UTC().Format(time.RFC3339), 300 + }) 301 + } 302 + 303 + func (s *Server) buildSignJWTRequestMsg(requestID string, payloadB64 string, aud string, lxm string) ([]byte, error) { 304 + return json.Marshal(wsSignJWTRequest{ 305 + Type: "sign_jwt_request", 306 + RequestID: requestID, 307 + JWTPayload: payloadB64, 308 + Aud: aud, 309 + Lxm: lxm, 290 310 }) 291 311 } 292 312
+1
server/server.go
··· 483 483 w.Header().Set("Access-Control-Allow-Headers", "*") 484 484 w.Header().Set("Access-Control-Allow-Methods", "*") 485 485 w.Header().Set("Access-Control-Allow-Credentials", "true") 486 + w.Header().Set("Access-Control-Expose-Headers", "Atproto-Proxy-Type,Atproto-Repo-Rev,Atproto-Content-Type,Content-Type,Content-Length,WWW-Authenticate,DPoP-Nonce,X-Ratelimit-Limit,X-Ratelimit-Remaining,X-Ratelimit-Reset") 486 487 w.Header().Set("Access-Control-Max-Age", "100000000") 487 488 if r.Method == http.MethodOptions { 488 489 w.WriteHeader(http.StatusNoContent)
+20 -20
server/templates/account.html
··· 596 596 signerMsg.textContent = ""; 597 597 } 598 598 599 - function showPending(ops) { 600 - if (ops && ops.length > 0) { 601 - pendingOpsEl.textContent = ops 602 - .map( 603 - (op) => 604 - op.type + 605 - " " + 606 - op.collection + 607 - (op.rkey ? "/" + op.rkey : ""), 608 - ) 609 - .join(", "); 610 - } else { 611 - pendingOpsEl.textContent = "(details unavailable)"; 612 - } 599 + function showPending(summary) { 600 + pendingOpsEl.textContent = summary || "(details unavailable)"; 613 601 pendingEl.style.display = ""; 614 602 } 615 603 ··· 791 779 } 792 780 793 781 pendingRequestId = requestId; 794 - showPending(ops); 782 + const summary = opsToSummary(ops); 783 + showPending(summary); 795 784 showNotification( 796 785 "Vow — Signing Request", 797 - opsToSummary(ops), 786 + summary, 798 787 ); 799 788 800 789 try { ··· 876 865 // --------------------------------------------------------------------------- 877 866 878 867 async function handleSignJWTRequest(msg) { 879 - const { requestId, jwtPayload } = msg; 868 + const { requestId, jwtPayload, aud, lxm } = msg; 880 869 881 870 if (!requestId || !jwtPayload) { 882 871 console.warn( ··· 896 885 } 897 886 898 887 pendingRequestId = requestId; 899 - showPending(null); // No ops for JWTs 888 + const summary = "Service Auth: " + (lxm || "Generic") + " for " + aud; 889 + showPending(summary); 900 890 showNotification( 901 891 "Vow — Service Authentication", 902 - "An app is requesting authentication. Please confirm with your passkey.", 892 + summary, 903 893 ); 904 894 905 895 try { ··· 1016 1006 if (!ops || ops.length === 0) 1017 1007 return "A signing request needs your approval."; 1018 1008 return ops 1019 - .map((op) => op.type + " " + op.collection) 1009 + .map((op) => { 1010 + let action = op.type.split("#")[1] || op.type; 1011 + // Prettify common ATProto method names 1012 + if (action === "create") action = "Create"; 1013 + if (action === "update") action = "Update"; 1014 + if (action === "delete") action = "Delete"; 1015 + 1016 + let s = action + " " + op.collection; 1017 + if (op.rkey) s += "/" + op.rkey; 1018 + return s; 1019 + }) 1020 1020 .join(", "); 1021 1021 } 1022 1022