~emersion/sinwon

63aed4af8bd1dac9ca730b31ced517c54ca49388 — Simon Ser 7 months ago d8d9387
Add button to rotate client secret
2 files changed, 45 insertions(+), 26 deletions(-)

M client.go
M template/manage-client.html
M client.go => client.go +42 -26
@@ 66,39 66,29 @@ func manageClient(w http.ResponseWriter, req *http.Request) {
		return
	}

	client.ClientName = req.PostFormValue("client_name")
	client.ClientURI = req.PostFormValue("client_uri")
	client.RedirectURIs = req.PostFormValue("redirect_uris")
	isPublic := req.PostFormValue("client_type") == "public"
	_, rotate := req.PostForm["rotate"]

	for _, s := range strings.Split(client.RedirectURIs, "\n") {
		if s == "" {
			continue
		}
		u, err := url.Parse(s)
		if err != nil {
	var isPublic bool
	if client.ID != 0 {
		isPublic = client.IsPublic()
	} else {
		isPublic = req.PostFormValue("client_type") == "public"
	}

	if !rotate {
		client.ClientName = req.PostFormValue("client_name")
		client.ClientURI = req.PostFormValue("client_uri")
		client.RedirectURIs = req.PostFormValue("redirect_uris")

		if err := validateAllowedRedirectURIs(client.RedirectURIs); err != nil {
			// TODO: nicer error message
			http.Error(w, fmt.Sprintf("Invalid redirect URI %q: %v", s, err), http.StatusBadRequest)
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		switch u.Scheme {
		case "https":
			// ok
		case "http":
			if u.Host != "localhost" {
				http.Error(w, "Only http://localhost is allowed for insecure HTTP URIs", http.StatusBadRequest)
				return
			}
		default:
			if !strings.Contains(u.Scheme, ".") {
				http.Error(w, "Only private-use URIs referring to domain names are allowed", http.StatusBadRequest)
				return
			}
		}
	}

	var clientSecret string
	if client.ID == 0 {
	if client.ID == 0 || rotate {
		clientSecret, err = client.Generate(isPublic)
		if err != nil {
			httpError(w, err)


@@ 128,6 118,32 @@ func manageClient(w http.ResponseWriter, req *http.Request) {
	}
}

func validateAllowedRedirectURIs(rawRedirectURIs string) error {
	for _, s := range strings.Split(rawRedirectURIs, "\n") {
		if s == "" {
			continue
		}
		u, err := url.Parse(s)
		if err != nil {
			// TODO: nicer error message
			return fmt.Errorf("Invalid redirect URI %q: %v", s, err)
		}
		switch u.Scheme {
		case "https":
			// ok
		case "http":
			if u.Host != "localhost" {
				return fmt.Errorf("Only http://localhost is allowed for insecure HTTP URIs")
			}
		default:
			if !strings.Contains(u.Scheme, ".") {
				return fmt.Errorf("Only private-use URIs referring to domain names are allowed")
			}
		}
	}
	return nil
}

func revokeClient(w http.ResponseWriter, req *http.Request) {
	ctx := req.Context()
	db := dbFromContext(ctx)

M template/manage-client.html => template/manage-client.html +3 -0
@@ 46,6 46,9 @@
		{{ end }}
	</button>
	{{ if .Client.ID }}
		{{ if not .Client.IsPublic }}
			<button type="submit" name="rotate">Rotate client secret</button>
		{{ end }}
		<button type="submit" name="delete">Delete client</button>
	{{ end }}
</form>