Fix spelling errors in docs
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
bin
 | 
			
		||||
dist
 | 
			
		||||
_output
 | 
			
		||||
.idea
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ connectors:
 | 
			
		||||
    # server provides access for anonymous auth.
 | 
			
		||||
    # Please note that if the bind password contains a `$`, it has to be saved in an
 | 
			
		||||
    # environment variable which should be given as the value to `bindPW`.
 | 
			
		||||
    bindDN: uid=seviceaccount,cn=users,dc=example,dc=com
 | 
			
		||||
    bindDN: uid=serviceaccount,cn=users,dc=example,dc=com
 | 
			
		||||
    bindPW: password
 | 
			
		||||
 | 
			
		||||
    # The attribute to display in the provided password prompt. If unset, will
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ Prominent examples of OpenID Connect providers include Google Accounts, Salesfor
 | 
			
		||||
 | 
			
		||||
This connector does not support the "groups" claim. Progress for this is tracked in [issue #1065][issue-1065].
 | 
			
		||||
 | 
			
		||||
When using refresh tokens, changes to the upstream claims aren't propegated to the id_token returned by dex. If a user's email changes, the "email" claim returned by dex won't change unless the user logs in again. Progress for this is tracked in [issue #863][issue-863].
 | 
			
		||||
When using refresh tokens, changes to the upstream claims aren't propagated to the id_token returned by dex. If a user's email changes, the "email" claim returned by dex won't change unless the user logs in again. Progress for this is tracked in [issue #863][issue-863].
 | 
			
		||||
 | 
			
		||||
## Configuration
 | 
			
		||||
 | 
			
		||||
@@ -36,7 +36,7 @@ connectors:
 | 
			
		||||
 | 
			
		||||
    # Some providers require passing client_secret via POST parameters instead
 | 
			
		||||
    # of basic auth, despite the OAuth2 RFC discouraging it. Many of these
 | 
			
		||||
    # cases are caught internally, but some may need to uncommented the
 | 
			
		||||
    # cases are caught internally, but some may need to uncomment the
 | 
			
		||||
    # following field.
 | 
			
		||||
    #
 | 
			
		||||
    # basicAuthUnsupported: true
 | 
			
		||||
@@ -56,7 +56,7 @@ connectors:
 | 
			
		||||
    #  - email
 | 
			
		||||
    #  - groups
 | 
			
		||||
 | 
			
		||||
    # Some providers return claims without "email_verified", when they had no usage of emails verification in enrollement process
 | 
			
		||||
    # Some providers return claims without "email_verified", when they had no usage of emails verification in enrollment process
 | 
			
		||||
    # or if they are acting as a proxy for another IDP etc AWS Cognito with an upstream SAML IDP
 | 
			
		||||
    # This can be overridden with the below option
 | 
			
		||||
    # insecureSkipEmailVerified: true 
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ in with GitHub.
 | 
			
		||||
 | 
			
		||||
## The problem
 | 
			
		||||
 | 
			
		||||
When dex is federaing to an upstream identity provider (IDP), we want to ensure
 | 
			
		||||
When dex is federating to an upstream identity provider (IDP), we want to ensure
 | 
			
		||||
claims being passed onto clients remain fresh. This includes data such as Google
 | 
			
		||||
accounts display names, LDAP group membership, account deactivations. Changes to
 | 
			
		||||
these on an upstream IDP should always be reflected in the claims dex passes to
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,7 @@ func (h *healthChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	h.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		h.s.renderError(w, http.StatusInternalServerError, "Health check failed.")
 | 
			
		||||
		h.s.renderError(r, w, http.StatusInternalServerError, "Health check failed.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Fprintf(w, "Health check passed in %s", t)
 | 
			
		||||
@@ -112,13 +112,13 @@ func (s *Server) handlePublicKeys(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	keys, err := s.storage.GetKeys()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("failed to get keys: %v", err)
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Internal server error.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if keys.SigningKeyPub == nil {
 | 
			
		||||
		s.logger.Errorf("No public keys found.")
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Internal server error.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -133,7 +133,7 @@ func (s *Server) handlePublicKeys(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	data, err := json.MarshalIndent(jwks, "", "  ")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("failed to marshal discovery data: %v", err)
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Internal server error.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	maxAge := keys.NextRotation.Sub(s.now())
 | 
			
		||||
@@ -214,7 +214,7 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
			status = err.Status()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.renderError(w, status, err.Error())
 | 
			
		||||
		s.renderError(r, w, status, err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -226,14 +226,14 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	authReq.Expiry = s.now().Add(s.authRequestsValidFor)
 | 
			
		||||
	if err := s.storage.CreateAuthRequest(*authReq); err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to create authorization request: %v", err)
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Failed to connect to the database.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Failed to connect to the database.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	connectors, err := s.storage.ListConnectors()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to get list of connectors: %v", err)
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Failed to retrieve connector list.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Failed to retrieve connector list.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -271,7 +271,7 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := s.templates.login(w, connectorInfos); err != nil {
 | 
			
		||||
	if err := s.templates.login(r, w, connectorInfos, r.URL.Path); err != nil {
 | 
			
		||||
		s.logger.Errorf("Server template error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -281,7 +281,7 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	conn, err := s.getConnector(connID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to create authorization request: %v", err)
 | 
			
		||||
		s.renderError(w, http.StatusBadRequest, "Requested resource does not exist")
 | 
			
		||||
		s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -291,9 +291,9 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to get auth request: %v", err)
 | 
			
		||||
		if err == storage.ErrNotFound {
 | 
			
		||||
			s.renderError(w, http.StatusBadRequest, "Login session expired.")
 | 
			
		||||
			s.renderError(r, w, http.StatusBadRequest, "Login session expired.")
 | 
			
		||||
		} else {
 | 
			
		||||
			s.renderError(w, http.StatusInternalServerError, "Database error.")
 | 
			
		||||
			s.renderError(r, w, http.StatusInternalServerError, "Database error.")
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -306,7 +306,7 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		}
 | 
			
		||||
		if err := s.storage.UpdateAuthRequest(authReqID, updater); err != nil {
 | 
			
		||||
			s.logger.Errorf("Failed to set connector ID on auth request: %v", err)
 | 
			
		||||
			s.renderError(w, http.StatusInternalServerError, "Database error.")
 | 
			
		||||
			s.renderError(r, w, http.StatusInternalServerError, "Database error.")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -324,19 +324,19 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
			callbackURL, err := conn.LoginURL(scopes, s.absURL("/callback"), authReqID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				s.logger.Errorf("Connector %q returned error when creating callback: %v", connID, err)
 | 
			
		||||
				s.renderError(w, http.StatusInternalServerError, "Login error.")
 | 
			
		||||
				s.renderError(r, w, http.StatusInternalServerError, "Login error.")
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			http.Redirect(w, r, callbackURL, http.StatusFound)
 | 
			
		||||
		case connector.PasswordConnector:
 | 
			
		||||
			if err := s.templates.password(w, r.URL.String(), "", usernamePrompt(conn), false, showBacklink); err != nil {
 | 
			
		||||
			if err := s.templates.password(r, w, r.URL.String(), "", usernamePrompt(conn), false, showBacklink, r.URL.Path); err != nil {
 | 
			
		||||
				s.logger.Errorf("Server template error: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		case connector.SAMLConnector:
 | 
			
		||||
			action, value, err := conn.POSTData(scopes, authReqID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				s.logger.Errorf("Creating SAML data: %v", err)
 | 
			
		||||
				s.renderError(w, http.StatusInternalServerError, "Connector Login Error")
 | 
			
		||||
				s.renderError(r, w, http.StatusInternalServerError, "Connector Login Error")
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -358,12 +358,12 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
			  </body>
 | 
			
		||||
			  </html>`, action, value, authReqID)
 | 
			
		||||
		default:
 | 
			
		||||
			s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
 | 
			
		||||
			s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.")
 | 
			
		||||
		}
 | 
			
		||||
	case http.MethodPost:
 | 
			
		||||
		passwordConnector, ok := conn.Connector.(connector.PasswordConnector)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
 | 
			
		||||
			s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -373,11 +373,11 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		identity, ok, err := passwordConnector.Login(r.Context(), scopes, username, password)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			s.logger.Errorf("Failed to login user: %v", err)
 | 
			
		||||
			s.renderError(w, http.StatusInternalServerError, fmt.Sprintf("Login error: %v", err))
 | 
			
		||||
			s.renderError(r, w, http.StatusInternalServerError, fmt.Sprintf("Login error: %v", err))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if !ok {
 | 
			
		||||
			if err := s.templates.password(w, r.URL.String(), username, usernamePrompt(passwordConnector), true, showBacklink); err != nil {
 | 
			
		||||
			if err := s.templates.password(r, w, r.URL.String(), username, usernamePrompt(passwordConnector), true, showBacklink, r.URL.Path); err != nil {
 | 
			
		||||
				s.logger.Errorf("Server template error: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
@@ -385,13 +385,13 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		redirectURL, err := s.finalizeLogin(identity, authReq, conn.Connector)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			s.logger.Errorf("Failed to finalize login: %v", err)
 | 
			
		||||
			s.renderError(w, http.StatusInternalServerError, "Login error.")
 | 
			
		||||
			s.renderError(r, w, http.StatusInternalServerError, "Login error.")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		http.Redirect(w, r, redirectURL, http.StatusSeeOther)
 | 
			
		||||
	default:
 | 
			
		||||
		s.renderError(w, http.StatusBadRequest, "Unsupported request method.")
 | 
			
		||||
		s.renderError(r, w, http.StatusBadRequest, "Unsupported request method.")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -400,16 +400,16 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
 | 
			
		||||
	switch r.Method {
 | 
			
		||||
	case http.MethodGet: // OAuth2 callback
 | 
			
		||||
		if authID = r.URL.Query().Get("state"); authID == "" {
 | 
			
		||||
			s.renderError(w, http.StatusBadRequest, "User session error.")
 | 
			
		||||
			s.renderError(r, w, http.StatusBadRequest, "User session error.")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	case http.MethodPost: // SAML POST binding
 | 
			
		||||
		if authID = r.PostFormValue("RelayState"); authID == "" {
 | 
			
		||||
			s.renderError(w, http.StatusBadRequest, "User session error.")
 | 
			
		||||
			s.renderError(r, w, http.StatusBadRequest, "User session error.")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		s.renderError(w, http.StatusBadRequest, "Method not supported")
 | 
			
		||||
		s.renderError(r, w, http.StatusBadRequest, "Method not supported")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -417,24 +417,24 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if err == storage.ErrNotFound {
 | 
			
		||||
			s.logger.Errorf("Invalid 'state' parameter provided: %v", err)
 | 
			
		||||
			s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
 | 
			
		||||
			s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		s.logger.Errorf("Failed to get auth request: %v", err)
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Database error.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Database error.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if connID := mux.Vars(r)["connector"]; connID != "" && connID != authReq.ConnectorID {
 | 
			
		||||
		s.logger.Errorf("Connector mismatch: authentication started with id %q, but callback for id %q was triggered", authReq.ConnectorID, connID)
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Requested resource does not exist.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn, err := s.getConnector(authReq.ConnectorID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to get connector with id %q : %v", authReq.ConnectorID, err)
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Requested resource does not exist.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -443,32 +443,32 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
 | 
			
		||||
	case connector.CallbackConnector:
 | 
			
		||||
		if r.Method != http.MethodGet {
 | 
			
		||||
			s.logger.Errorf("SAML request mapped to OAuth2 connector")
 | 
			
		||||
			s.renderError(w, http.StatusBadRequest, "Invalid request")
 | 
			
		||||
			s.renderError(r, w, http.StatusBadRequest, "Invalid request")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		identity, err = conn.HandleCallback(parseScopes(authReq.Scopes), r)
 | 
			
		||||
	case connector.SAMLConnector:
 | 
			
		||||
		if r.Method != http.MethodPost {
 | 
			
		||||
			s.logger.Errorf("OAuth2 request mapped to SAML connector")
 | 
			
		||||
			s.renderError(w, http.StatusBadRequest, "Invalid request")
 | 
			
		||||
			s.renderError(r, w, http.StatusBadRequest, "Invalid request")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		identity, err = conn.HandlePOST(parseScopes(authReq.Scopes), r.PostFormValue("SAMLResponse"), authReq.ID)
 | 
			
		||||
	default:
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Requested resource does not exist.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to authenticate: %v", err)
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to authenticate: %v", err))
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, fmt.Sprintf("Failed to authenticate: %v", err))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	redirectURL, err := s.finalizeLogin(identity, authReq, conn.Connector)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to finalize login: %v", err)
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Login error.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Login error.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -511,12 +511,12 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	authReq, err := s.storage.GetAuthRequest(r.FormValue("req"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to get auth request: %v", err)
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Database error.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Database error.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !authReq.LoggedIn {
 | 
			
		||||
		s.logger.Errorf("Auth request does not have an identity for approval")
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Login process not yet finalized.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Login process not yet finalized.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -529,15 +529,15 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		client, err := s.storage.GetClient(authReq.ClientID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			s.logger.Errorf("Failed to get client %q: %v", authReq.ClientID, err)
 | 
			
		||||
			s.renderError(w, http.StatusInternalServerError, "Failed to retrieve client.")
 | 
			
		||||
			s.renderError(r, w, http.StatusInternalServerError, "Failed to retrieve client.")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if err := s.templates.approval(w, authReq.ID, authReq.Claims.Username, client.Name, authReq.Scopes); err != nil {
 | 
			
		||||
		if err := s.templates.approval(r, w, authReq.ID, authReq.Claims.Username, client.Name, authReq.Scopes, r.URL.Path); err != nil {
 | 
			
		||||
			s.logger.Errorf("Server template error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	case http.MethodPost:
 | 
			
		||||
		if r.FormValue("approval") != "approve" {
 | 
			
		||||
			s.renderError(w, http.StatusInternalServerError, "Approval rejected.")
 | 
			
		||||
			s.renderError(r, w, http.StatusInternalServerError, "Approval rejected.")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		s.sendCodeResponse(w, r, authReq)
 | 
			
		||||
@@ -546,22 +546,22 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
 | 
			
		||||
func (s *Server) sendCodeResponse(w http.ResponseWriter, r *http.Request, authReq storage.AuthRequest) {
 | 
			
		||||
	if s.now().After(authReq.Expiry) {
 | 
			
		||||
		s.renderError(w, http.StatusBadRequest, "User session has expired.")
 | 
			
		||||
		s.renderError(r, w, http.StatusBadRequest, "User session has expired.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := s.storage.DeleteAuthRequest(authReq.ID); err != nil {
 | 
			
		||||
		if err != storage.ErrNotFound {
 | 
			
		||||
			s.logger.Errorf("Failed to delete authorization request: %v", err)
 | 
			
		||||
			s.renderError(w, http.StatusInternalServerError, "Internal server error.")
 | 
			
		||||
			s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
 | 
			
		||||
		} else {
 | 
			
		||||
			s.renderError(w, http.StatusBadRequest, "User session error.")
 | 
			
		||||
			s.renderError(r, w, http.StatusBadRequest, "User session error.")
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	u, err := url.Parse(authReq.RedirectURI)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.renderError(w, http.StatusInternalServerError, "Invalid redirect URI.")
 | 
			
		||||
		s.renderError(r, w, http.StatusInternalServerError, "Invalid redirect URI.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -598,14 +598,14 @@ func (s *Server) sendCodeResponse(w http.ResponseWriter, r *http.Request, authRe
 | 
			
		||||
			}
 | 
			
		||||
			if err := s.storage.CreateAuthCode(code); err != nil {
 | 
			
		||||
				s.logger.Errorf("Failed to create auth code: %v", err)
 | 
			
		||||
				s.renderError(w, http.StatusInternalServerError, "Internal server error.")
 | 
			
		||||
				s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Implicit and hybrid flows that try to use the OOB redirect URI are
 | 
			
		||||
			// rejected earlier. If we got here we're using the code flow.
 | 
			
		||||
			if authReq.RedirectURI == redirectURIOOB {
 | 
			
		||||
				if err := s.templates.oob(w, code.ID); err != nil {
 | 
			
		||||
				if err := s.templates.oob(r, w, code.ID, r.URL.Path); err != nil {
 | 
			
		||||
					s.logger.Errorf("Server template error: %v", err)
 | 
			
		||||
				}
 | 
			
		||||
				return
 | 
			
		||||
@@ -1119,8 +1119,8 @@ func (s *Server) writeAccessToken(w http.ResponseWriter, idToken, accessToken, r
 | 
			
		||||
	w.Write(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Server) renderError(w http.ResponseWriter, status int, description string) {
 | 
			
		||||
	if err := s.templates.err(w, status, description); err != nil {
 | 
			
		||||
func (s *Server) renderError(r *http.Request, w http.ResponseWriter, status int, description string) {
 | 
			
		||||
	if err := s.templates.err(r, w, status, description); err != nil {
 | 
			
		||||
		s.logger.Errorf("Server template error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,9 @@ import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
@@ -94,7 +96,7 @@ func loadWebConfig(c webConfig) (static, theme http.Handler, templates *template
 | 
			
		||||
		c.dir = "./web"
 | 
			
		||||
	}
 | 
			
		||||
	if c.logoURL == "" {
 | 
			
		||||
		c.logoURL = join(c.issuerURL, "theme/logo.png")
 | 
			
		||||
		c.logoURL = "theme/logo.png"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := dirExists(c.dir); err != nil {
 | 
			
		||||
@@ -136,10 +138,15 @@ func loadTemplates(c webConfig, templatesDir string) (*templates, error) {
 | 
			
		||||
		return nil, fmt.Errorf("no files in template dir %q", templatesDir)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	issuerURL, err := url.Parse(c.issuerURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("error parsing issuerURL: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	funcs := map[string]interface{}{
 | 
			
		||||
		"issuer": func() string { return c.issuer },
 | 
			
		||||
		"logo":   func() string { return c.logoURL },
 | 
			
		||||
		"url":    func(s string) string { return join(c.issuerURL, s) },
 | 
			
		||||
		"url":    func(reqPath, assetPath string) string { return relativeURL(issuerURL.Path, reqPath, assetPath) },
 | 
			
		||||
		"lower":  strings.ToLower,
 | 
			
		||||
		"extra":  func(k string) string { return c.extra[k] },
 | 
			
		||||
	}
 | 
			
		||||
@@ -166,6 +173,69 @@ func loadTemplates(c webConfig, templatesDir string) (*templates, error) {
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// relativeURL returns the URL of the asset relative to the URL of the request path.
 | 
			
		||||
// The serverPath is consulted to trim any prefix due in case it is not listening
 | 
			
		||||
// to the root path.
 | 
			
		||||
//
 | 
			
		||||
// Algorithm:
 | 
			
		||||
// 1. Remove common prefix of serverPath and reqPath
 | 
			
		||||
// 2. Remove common prefix of assetPath and reqPath
 | 
			
		||||
// 3. For each part of reqPath remaining(minus one), go up one level (..)
 | 
			
		||||
// 4. For each part of assetPath remaining, append it to result
 | 
			
		||||
//
 | 
			
		||||
//eg
 | 
			
		||||
//server listens at localhost/dex so serverPath is dex
 | 
			
		||||
//reqPath is /dex/auth
 | 
			
		||||
//assetPath is static/main.css
 | 
			
		||||
//relativeURL("/dex", "/dex/auth", "static/main.css") = "../static/main.css"
 | 
			
		||||
func relativeURL(serverPath, reqPath, assetPath string) string {
 | 
			
		||||
 | 
			
		||||
	splitPath := func(p string) []string {
 | 
			
		||||
		res := []string{}
 | 
			
		||||
		parts := strings.Split(path.Clean(p), "/")
 | 
			
		||||
		for _, part := range parts {
 | 
			
		||||
			if part != "" {
 | 
			
		||||
				res = append(res, part)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return res
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stripCommonParts := func(s1, s2 []string) ([]string, []string) {
 | 
			
		||||
		min := len(s1)
 | 
			
		||||
		if len(s2) < min {
 | 
			
		||||
			min = len(s2)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		splitIndex := min
 | 
			
		||||
		for i := 0; i < min; i++ {
 | 
			
		||||
			if s1[i] != s2[i] {
 | 
			
		||||
				splitIndex = i
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return s1[splitIndex:], s2[splitIndex:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	server, req, asset := splitPath(serverPath), splitPath(reqPath), splitPath(assetPath)
 | 
			
		||||
 | 
			
		||||
	// Remove common prefix of request path with server path
 | 
			
		||||
	server, req = stripCommonParts(server, req)
 | 
			
		||||
 | 
			
		||||
	// Remove common prefix of request path with asset path
 | 
			
		||||
	asset, req = stripCommonParts(asset, req)
 | 
			
		||||
 | 
			
		||||
	// For each part of the request remaining (minus one) -> go up one level (..)
 | 
			
		||||
	// For each part of the asset remaining               -> append it
 | 
			
		||||
	var relativeURL string
 | 
			
		||||
	for i := 0; i < len(req)-1; i++ {
 | 
			
		||||
		relativeURL = path.Join("..", relativeURL)
 | 
			
		||||
	}
 | 
			
		||||
	relativeURL = path.Join(relativeURL, path.Join(asset...))
 | 
			
		||||
 | 
			
		||||
	return relativeURL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var scopeDescriptions = map[string]string{
 | 
			
		||||
	"offline_access": "Have offline access",
 | 
			
		||||
	"profile":        "View basic profile information",
 | 
			
		||||
@@ -184,26 +254,28 @@ func (n byName) Len() int           { return len(n) }
 | 
			
		||||
func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name }
 | 
			
		||||
func (n byName) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
 | 
			
		||||
 | 
			
		||||
func (t *templates) login(w http.ResponseWriter, connectors []connectorInfo) error {
 | 
			
		||||
func (t *templates) login(r *http.Request, w http.ResponseWriter, connectors []connectorInfo, reqPath string) error {
 | 
			
		||||
	sort.Sort(byName(connectors))
 | 
			
		||||
	data := struct {
 | 
			
		||||
		Connectors []connectorInfo
 | 
			
		||||
	}{connectors}
 | 
			
		||||
		ReqPath    string
 | 
			
		||||
	}{connectors, r.URL.Path}
 | 
			
		||||
	return renderTemplate(w, t.loginTmpl, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *templates) password(w http.ResponseWriter, postURL, lastUsername, usernamePrompt string, lastWasInvalid, showBacklink bool) error {
 | 
			
		||||
func (t *templates) password(r *http.Request, w http.ResponseWriter, postURL, lastUsername, usernamePrompt string, lastWasInvalid, showBacklink bool, reqPath string) error {
 | 
			
		||||
	data := struct {
 | 
			
		||||
		PostURL        string
 | 
			
		||||
		BackLink       bool
 | 
			
		||||
		Username       string
 | 
			
		||||
		UsernamePrompt string
 | 
			
		||||
		Invalid        bool
 | 
			
		||||
	}{postURL, showBacklink, lastUsername, usernamePrompt, lastWasInvalid}
 | 
			
		||||
		ReqPath        string
 | 
			
		||||
	}{postURL, showBacklink, lastUsername, usernamePrompt, lastWasInvalid, r.URL.Path}
 | 
			
		||||
	return renderTemplate(w, t.passwordTmpl, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *templates) approval(w http.ResponseWriter, authReqID, username, clientName string, scopes []string) error {
 | 
			
		||||
func (t *templates) approval(r *http.Request, w http.ResponseWriter, authReqID, username, clientName string, scopes []string, reqPath string) error {
 | 
			
		||||
	accesses := []string{}
 | 
			
		||||
	for _, scope := range scopes {
 | 
			
		||||
		access, ok := scopeDescriptions[scope]
 | 
			
		||||
@@ -217,23 +289,26 @@ func (t *templates) approval(w http.ResponseWriter, authReqID, username, clientN
 | 
			
		||||
		Client    string
 | 
			
		||||
		AuthReqID string
 | 
			
		||||
		Scopes    []string
 | 
			
		||||
	}{username, clientName, authReqID, accesses}
 | 
			
		||||
		ReqPath   string
 | 
			
		||||
	}{username, clientName, authReqID, accesses, r.URL.Path}
 | 
			
		||||
	return renderTemplate(w, t.approvalTmpl, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *templates) oob(w http.ResponseWriter, code string) error {
 | 
			
		||||
func (t *templates) oob(r *http.Request, w http.ResponseWriter, code string, reqPath string) error {
 | 
			
		||||
	data := struct {
 | 
			
		||||
		Code string
 | 
			
		||||
	}{code}
 | 
			
		||||
		Code    string
 | 
			
		||||
		ReqPath string
 | 
			
		||||
	}{code, r.URL.Path}
 | 
			
		||||
	return renderTemplate(w, t.oobTmpl, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *templates) err(w http.ResponseWriter, errCode int, errMsg string) error {
 | 
			
		||||
func (t *templates) err(r *http.Request, w http.ResponseWriter, errCode int, errMsg string) error {
 | 
			
		||||
	w.WriteHeader(errCode)
 | 
			
		||||
	data := struct {
 | 
			
		||||
		ErrType string
 | 
			
		||||
		ErrMsg  string
 | 
			
		||||
	}{http.StatusText(errCode), errMsg}
 | 
			
		||||
		ReqPath string
 | 
			
		||||
	}{http.StatusText(errCode), errMsg, r.URL.Path}
 | 
			
		||||
	if err := t.errorTmpl.Execute(w, data); err != nil {
 | 
			
		||||
		return fmt.Errorf("Error rendering template %s: %s", t.errorTmpl.Name(), err)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,44 @@
 | 
			
		||||
package server
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
func TestRelativeURL(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name       string
 | 
			
		||||
		serverPath string
 | 
			
		||||
		reqPath    string
 | 
			
		||||
		assetPath  string
 | 
			
		||||
		expected   string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:       "server-root-req-one-level-asset-two-level",
 | 
			
		||||
			serverPath: "/",
 | 
			
		||||
			reqPath:    "/auth",
 | 
			
		||||
			assetPath:  "/theme/main.css",
 | 
			
		||||
			expected:   "theme/main.css",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "server-one-level-req-one-level-asset-two-level",
 | 
			
		||||
			serverPath: "/dex",
 | 
			
		||||
			reqPath:    "/dex/auth",
 | 
			
		||||
			assetPath:  "/theme/main.css",
 | 
			
		||||
			expected:   "theme/main.css",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "server-root-req-two-level-asset-three-level",
 | 
			
		||||
			serverPath: "/dex",
 | 
			
		||||
			reqPath:    "/dex/auth/connector",
 | 
			
		||||
			assetPath:  "assets/css/main.css",
 | 
			
		||||
			expected:   "../assets/css/main.css",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			actual := relativeURL(test.serverPath, test.reqPath, test.assetPath)
 | 
			
		||||
			if actual != test.expected {
 | 
			
		||||
				t.Fatalf("Got '%s'. Expected '%s'", actual, test.expected)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,15 +5,15 @@
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
 | 
			
		||||
    <title>{{ issuer }}</title>
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <link href="{{ url "static/main.css" }}" rel="stylesheet">
 | 
			
		||||
    <link href="{{ url "theme/styles.css" }}" rel="stylesheet">
 | 
			
		||||
    <link rel="icon" href="{{ url "theme/favicon.png" }}">
 | 
			
		||||
    <link href="{{ url .ReqPath "static/main.css" }}" rel="stylesheet">
 | 
			
		||||
    <link href="{{ url .ReqPath "theme/styles.css" }}" rel="stylesheet">
 | 
			
		||||
    <link rel="icon" href="{{ url .ReqPath "theme/favicon.png" }}">
 | 
			
		||||
  </head>
 | 
			
		||||
 | 
			
		||||
  <body class="theme-body">
 | 
			
		||||
    <div class="theme-navbar">
 | 
			
		||||
      <div class="theme-navbar__logo-wrap">
 | 
			
		||||
        <img class="theme-navbar__logo" src="{{ logo }}">
 | 
			
		||||
        <img class="theme-navbar__logo" src="{{ url .ReqPath logo }}">
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user