Generates/Stores the device request and returns the device and user codes.

Signed-off-by: justin-slowik <justin.slowik@thermofisher.com>
This commit is contained in:
Justin Slowik
2020-01-16 10:55:07 -05:00
committed by justin-slowik
parent 11fc8568cb
commit 6d343e059b
14 changed files with 690 additions and 8 deletions

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"path"
@@ -15,12 +16,11 @@ import (
"time"
oidc "github.com/coreos/go-oidc"
"github.com/gorilla/mux"
jose "gopkg.in/square/go-jose.v2"
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/server/internal"
"github.com/dexidp/dex/storage"
"github.com/gorilla/mux"
jose "gopkg.in/square/go-jose.v2"
)
// newHealthChecker returns the healthz handler. The handler runs until the
@@ -1415,3 +1415,112 @@ func usernamePrompt(conn connector.PasswordConnector) string {
}
return "Username"
}
type deviceCodeResponse struct {
//The unique device code for device authentication
DeviceCode string `json:"device_code"`
//The code the user will exchange via a browser and log in
UserCode string `json:"user_code"`
//The url to verify the user code.
VerificationURI string `json:"verification_uri"`
//The lifetime of the device code
ExpireTime int `json:"expires_in"`
//How often the device is allowed to poll to verify that the user login occurred
PollInterval int `json:"interval"`
}
func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) {
//TODO replace with configurable values
expireIntervalSeconds := 300
requestsPerMinute := 5
switch r.Method {
case http.MethodPost:
err := r.ParseForm()
if err != nil {
message := "Could not parse Device Request body"
s.logger.Errorf("%s : %v", message, err)
respondWithError(w, message, err)
return
}
//Get the client id and scopes from the post
clientID := r.Form.Get("client_id")
scopes := r.Form["scope"]
s.logger.Infof("Received device request for client %v with scopes %v", clientID, scopes)
//Make device code
deviceCode := storage.NewDeviceCode()
//make user code
userCode := storage.NewUserCode()
//make a pkce verification code
pkceCode := storage.NewID()
//Generate the expire time
expireTime := time.Now().Add(time.Second * time.Duration(expireIntervalSeconds))
//Store the Device Request
deviceReq := storage.DeviceRequest{
UserCode: userCode,
DeviceCode: deviceCode,
ClientID: clientID,
Scopes: scopes,
PkceVerifier: pkceCode,
Expiry: expireTime,
}
if err := s.storage.CreateDeviceRequest(deviceReq); err != nil {
message := fmt.Sprintf("Failed to store device request %v", err)
s.logger.Errorf(message)
respondWithError(w, message, err)
return
}
//Store the device token
deviceToken := storage.DeviceToken{
DeviceCode: deviceCode,
Status: "pending",
Token: "",
Expiry: expireTime,
}
if err := s.storage.CreateDeviceToken(deviceToken); err != nil {
message := fmt.Sprintf("Failed to store device token %v", err)
s.logger.Errorf(message)
respondWithError(w, message, err)
return
}
code := deviceCodeResponse{
DeviceCode: deviceCode,
UserCode: userCode,
VerificationURI: path.Join(s.issuerURL.String(), "/device"),
ExpireTime: expireIntervalSeconds,
PollInterval: requestsPerMinute,
}
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
enc.Encode(code)
default:
s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.")
}
}
func respondWithError(w io.Writer, errorMessage string, err error) {
resp := struct {
Error string `json:"error"`
ErrorMessage string `json:"message"`
}{
Error: err.Error(),
ErrorMessage: errorMessage,
}
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
enc.Encode(resp)
}

View File

@@ -302,6 +302,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy)
handleWithCORS("/userinfo", s.handleUserInfo)
handleFunc("/auth", s.handleAuthorization)
handleFunc("/auth/{connector}", s.handleConnectorLogin)
handleFunc("/device/code", s.handleDeviceCode)
r.HandleFunc(path.Join(issuerURL.Path, "/callback"), func(w http.ResponseWriter, r *http.Request) {
// Strip the X-Remote-* headers to prevent security issues on
// misconfigured authproxy connector setups.
@@ -450,7 +451,8 @@ func (s *Server) startGarbageCollection(ctx context.Context, frequency time.Dura
if r, err := s.storage.GarbageCollect(now()); err != nil {
s.logger.Errorf("garbage collection failed: %v", err)
} else if r.AuthRequests > 0 || r.AuthCodes > 0 {
s.logger.Infof("garbage collection run, delete auth requests=%d, auth codes=%d", r.AuthRequests, r.AuthCodes)
s.logger.Infof("garbage collection run, delete auth requests=%d, auth codes=%d, device requests =%d, device tokens=%d",
r.AuthRequests, r.AuthCodes, r.DeviceRequests, r.DeviceTokens)
}
}
}