aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoradnano <[email protected]>2020-09-28 00:29:11 -0400
committeradnano <[email protected]>2020-09-28 00:29:11 -0400
commit622ea8e0f1e5f985e2e4f22b83bac8ed363897cb (patch)
tree50772f5a5050c8c15b80076a727656def4978a5b
parentRemove log.Print call (diff)
downloadgo-gemini-622ea8e0f1e5f985e2e4f22b83bac8ed363897cb.tar.xz
go-gemini-622ea8e0f1e5f985e2e4f22b83bac8ed363897cb.zip
Update documentation
-rw-r--r--README.md32
-rw-r--r--client.go8
-rw-r--r--examples/client/client.go4
3 files changed, 40 insertions, 4 deletions
diff --git a/README.md b/README.md
index a9dd0d1..cdf5f2f 100644
--- a/README.md
+++ b/README.md
@@ -82,5 +82,35 @@ certificate. See `examples/client` for an example.
Gemini takes advantage of client certificates for authentication.
-See `examples/auth` for an example server which authenticates its users with a
+If a server responds with `StatusCertificateRequired`, clients will generate a
+certificate for the site and resend the request with the provided certificate.
+In order for this to work, clients must specify the fields `CertificateStore`
+and `GetCertificate`:
+
+```go
+// Initialize the certificate store.
+client.CertificateStore = gmi.NewCertificateStore()
+// GetCertificate is called when a server requests a certificate.
+// The returned certificate, if not nil, will be used when resending the request.
+client.GetCertificate = func(hostname string, store gmi.CertificateStore) *tls.Certificate {
+ // If the certificate is in the store, return it
+ if cert, ok := store[hostname]; ok {
+ return cert
+ }
+ // Otherwise, generate a certificate
+ duration := time.Hour
+ cert, err := gmi.NewCertificate(hostname, duration)
+ if err != nil {
+ return nil
+ }
+ // Store and return the certificate
+ store[hostname] = &cert
+ return &cert
+}
+```
+
+Servers can then authenticate their clients with the fingerprint of their
+certificates.
+
+See `examples/auth` for an example server which authenticates its users with
username and password, and uses their client certificate to remember sessions.
diff --git a/client.go b/client.go
index a7bbbc5..f99735b 100644
--- a/client.go
+++ b/client.go
@@ -34,6 +34,7 @@ type Request struct {
Host string
// Certificate specifies the TLS certificate to use for the request.
+ // Request certificates take precedence over client certificates.
// This field is ignored by the server.
Certificate *tls.Certificate
@@ -188,8 +189,10 @@ type Client struct {
// CertificateStore contains all the certificates that the client has stored.
CertificateStore CertificateStore
- // GetCertificate, if not nil, will be called to determine which certificate
- // to use when the server responds with CertificateRequired.
+ // GetCertificate, if not nil, will be called when a server requests a certificate.
+ // The returned certificate will be used when sending the request again.
+ // If the certificate is nil, the request will not be sent again and
+ // the response will be returned.
GetCertificate func(hostname string, store CertificateStore) *tls.Certificate
// TrustCertificate, if not nil, will be called to determine whether the
@@ -205,6 +208,7 @@ func (c *Client) Send(req *Request) (*Response, error) {
InsecureSkipVerify: true,
MinVersion: tls.VersionTLS12,
GetClientCertificate: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
+ // Request certificates take precedence over client certificates
if req.Certificate != nil {
return req.Certificate, nil
}
diff --git a/examples/client/client.go b/examples/client/client.go
index e8a16be..3e88c73 100644
--- a/examples/client/client.go
+++ b/examples/client/client.go
@@ -49,15 +49,17 @@ func init() {
client.CertificateStore = gmi.NewCertificateStore()
client.GetCertificate = func(hostname string, store gmi.CertificateStore) *tls.Certificate {
+ // If the certificate is in the store, return it
if cert, ok := store[hostname]; ok {
return cert
}
- // Generate a certificate
+ // Otherwise, generate a certificate
duration := time.Hour
cert, err := gmi.NewCertificate(hostname, duration)
if err != nil {
return nil
}
+ // Store and return the certificate
store[hostname] = &cert
return &cert
}