add nonce endpoint

This commit is contained in:
AJ ONeal 2019-08-19 05:04:55 +00:00
parent 364de7114a
commit d6f5027480
1 changed files with 84 additions and 28 deletions

112
mockid.go
View File

@ -34,8 +34,13 @@ type PublicJWK struct {
Y string `json:"y"` Y string `json:"y"`
} }
var nonces map[string]int64
var jwksPrefix string var jwksPrefix string
func init() {
nonces = make(map[string]int64)
}
func main() { func main() {
done := make(chan bool) done := make(chan bool)
var port int var port int
@ -92,6 +97,34 @@ func main() {
os.Exit(1) os.Exit(1)
} }
http.HandleFunc("/api/new-nonce", func(w http.ResponseWriter, r *http.Request) {
baseURL := getBaseURL(r)
/*
res.statusCode = 200;
res.setHeader("Cache-Control", "max-age=0, no-cache, no-store");
// TODO
//res.setHeader("Date", "Sun, 10 Mar 2019 08:04:45 GMT");
// is this the expiration of the nonce itself? methinks maybe so
//res.setHeader("Expires", "Sun, 10 Mar 2019 08:04:45 GMT");
// TODO use one of the registered domains
//var indexUrl = "https://acme-staging-v02.api.letsencrypt.org/index"
*/
//var port = (state.config.ipc && state.config.ipc.port || state._ipc.port || undefined);
//var indexUrl = "http://localhost:" + port + "/index";
indexUrl := baseURL + "/index";
w.Header().Set("Link", "<" + indexUrl + ">;rel=\"index\"");
w.Header().Set("Cache-Control", "max-age=0, no-cache, no-store");
w.Header().Set("Pragma", "no-cache");
//res.setHeader("Strict-Transport-Security", "max-age=604800");
w.Header().Set("X-Frame-Options", "DENY")
issueNonce(w, r)
})
http.HandleFunc("/api/new-account", requireNonce(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Not Implemented", http.StatusNotImplemented)
}))
http.HandleFunc("/api/jwks", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/api/jwks", func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s %s", r.Method, r.Host, r.URL.Path) log.Printf("%s %s %s", r.Method, r.Host, r.URL.Path)
if "POST" != r.Method { if "POST" != r.Method {
@ -185,37 +218,20 @@ func main() {
return return
} }
var scheme string baseURL := getBaseURL(r)
if nil != r.TLS || "https" == r.Header.Get("X-Forwarded-Proto") {
scheme = "https://"
} else {
scheme = "http://"
}
w.Write([]byte(fmt.Sprintf( w.Write([]byte(fmt.Sprintf(
`{ "iss":%q, "jwks_url":%q }`, scheme+r.Host+"/", scheme+r.Host+"/.well-known/jwks.json", `{ "iss":%q, "jwks_url":%q }`, baseURL+"/", baseURL+"/.well-known/jwks.json",
))) )))
}) })
http.HandleFunc("/access_token", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/access_token", func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s\n", r.Method, r.URL.Path) log.Printf("%s %s\n", r.Method, r.URL.Path)
var scheme string _, _, token := genToken(getBaseURL(r), priv, r.URL.Query())
if nil != r.TLS || "https" == r.Header.Get("X-Forwarded-Proto") {
scheme = "https://"
} else {
scheme = "http://"
}
_, _, token := genToken(scheme+r.Host, priv, r.URL.Query())
fmt.Fprintf(w, token) fmt.Fprintf(w, token)
}) })
http.HandleFunc("/authorization_header", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/authorization_header", func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s\n", r.Method, r.URL.Path) log.Printf("%s %s\n", r.Method, r.URL.Path)
var scheme string
if nil != r.TLS || "https" == r.Header.Get("X-Forwarded-Proto") {
scheme = "https://"
} else {
scheme = "http://"
}
var header string var header string
headers, _ := r.URL.Query()["header"] headers, _ := r.URL.Query()["header"]
@ -233,7 +249,7 @@ func main() {
prefix = prefixes[0] prefix = prefixes[0]
} }
_, _, token := genToken(scheme+r.Host, priv, r.URL.Query()) _, _, token := genToken(getBaseURL(r), priv, r.URL.Query())
fmt.Fprintf(w, "%s: %s%s", header, prefix, token) fmt.Fprintf(w, "%s: %s%s", header, prefix, token)
}) })
@ -243,14 +259,9 @@ func main() {
}) })
http.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
var scheme string baseURL := getBaseURL(r)
if nil != r.TLS || "https" == r.Header.Get("X-Forwarded-Proto") {
scheme = "https://"
} else {
scheme = "http://"
}
log.Printf("%s %s\n", r.Method, r.URL.Path) log.Printf("%s %s\n", r.Method, r.URL.Path)
fmt.Fprintf(w, `{ "issuer": "%s", "jwks_uri": "%s/.well-known/jwks.json" }`, scheme+r.Host, scheme+r.Host) fmt.Fprintf(w, `{ "issuer": "%s", "jwks_uri": "%s/.well-known/jwks.json" }`, baseURL, baseURL)
}) })
http.HandleFunc("/.well-known/jwks.json", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/.well-known/jwks.json", func(w http.ResponseWriter, r *http.Request) {
@ -407,3 +418,48 @@ func thumbprintKey(pub *ecdsa.PublicKey) string {
sha := sha256.Sum256(minpub) sha := sha256.Sum256(minpub)
return base64.RawURLEncoding.EncodeToString(sha[:]) return base64.RawURLEncoding.EncodeToString(sha[:])
} }
func issueNonce(w http.ResponseWriter, r *http.Request) {
b := make([]byte, 16)
_, _ = rand.Read(b)
nonce := base64.RawURLEncoding.EncodeToString(b);
nonces[nonce] = time.Now().Unix()
w.Header().Set("Replay-Nonce", nonce);
}
func requireNonce(next http.HandlerFunc) http.HandlerFunc {
return func (w http.ResponseWriter, r *http.Request) {
nonce := r.Header.Get("Replay-Nonce")
// TODO expire nonces every so often
t := nonces[nonce]
if 0 == t {
http.Error(
w,
`{ "error": "invalid or expired nonce", "error_code": "ENONCE" }`,
http.StatusBadRequest,
)
return
}
delete(nonces, nonce)
issueNonce(w, r)
next(w, r);
}
}
func getBaseURL(r *http.Request) string {
var scheme string
if nil != r.TLS || "https" == r.Header.Get("X-Forwarded-Proto") {
scheme = "https:"
} else {
scheme = "http:"
}
return fmt.Sprintf(
"%s//%s",
scheme,
r.Host,
)
}