authenticate: add additional error details for hmac errors (#3878)

This commit is contained in:
Caleb Doxsey 2023-01-11 07:53:11 -07:00 committed by GitHub
parent 92b50683ff
commit bfcd15435f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 7 deletions

View file

@ -382,11 +382,6 @@ func (a *Authenticate) getOAuthCallback(w http.ResponseWriter, r *http.Request)
return nil, httputil.NewError(http.StatusBadRequest, fmt.Errorf("state malformed, size: %d", len(statePayload))) return nil, httputil.NewError(http.StatusBadRequest, fmt.Errorf("state malformed, size: %d", len(statePayload)))
} }
// verify that the returned timestamp is valid
if err := cryptutil.ValidTimestamp(statePayload[1]); err != nil {
return nil, httputil.NewError(http.StatusBadRequest, err)
}
// Use our AEAD construct to enforce secrecy and authenticity: // Use our AEAD construct to enforce secrecy and authenticity:
// mac: to validate the nonce again, and above timestamp // mac: to validate the nonce again, and above timestamp
// decrypt: to prevent leaking 'redirect_uri' to IdP or logs // decrypt: to prevent leaking 'redirect_uri' to IdP or logs
@ -401,6 +396,17 @@ func (a *Authenticate) getOAuthCallback(w http.ResponseWriter, r *http.Request)
return nil, httputil.NewError(http.StatusBadRequest, err) return nil, httputil.NewError(http.StatusBadRequest, err)
} }
// verify that the returned timestamp is valid
if err := cryptutil.ValidTimestamp(statePayload[1]); err != nil {
return nil, httputil.NewError(http.StatusBadRequest, err).WithDescription(fmt.Sprintf(`
The request expired. This may be because a login attempt took too long, or because the server's clock is out of sync.
Try again by following this link: [%s](%s).
Or contact your administrator.
`, redirectURL.String(), redirectURL.String()))
}
idp, err := options.GetIdentityProviderForID(redirectURL.Query().Get(urlutil.QueryIdentityProviderID)) idp, err := options.GetIdentityProviderForID(redirectURL.Query().Get(urlutil.QueryIdentityProviderID))
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -17,6 +17,7 @@ type HTTPError struct {
Status int Status int
// Err is the wrapped error. // Err is the wrapped error.
Err error Err error
Description string
// DebugURL is the URL to the debug endpoint. // DebugURL is the URL to the debug endpoint.
DebugURL *url.URL DebugURL *url.URL
// The request ID. // The request ID.
@ -26,7 +27,7 @@ type HTTPError struct {
} }
// NewError returns an error that contains a HTTP status and error. // NewError returns an error that contains a HTTP status and error.
func NewError(status int, err error) error { func NewError(status int, err error) *HTTPError {
return &HTTPError{Status: status, Err: err} return &HTTPError{Status: status, Err: err}
} }
@ -54,6 +55,7 @@ func (e *HTTPError) ErrorResponse(ctx context.Context, w http.ResponseWriter, r
response := struct { response := struct {
Status int Status int
StatusText string `json:"-"` StatusText string `json:"-"`
Description string `json:"description,omitempty"`
RequestID string `json:",omitempty"` RequestID string `json:",omitempty"`
CanDebug bool `json:"-"` CanDebug bool `json:"-"`
DebugURL *url.URL `json:",omitempty"` DebugURL *url.URL `json:",omitempty"`
@ -61,6 +63,7 @@ func (e *HTTPError) ErrorResponse(ctx context.Context, w http.ResponseWriter, r
}{ }{
Status: e.Status, Status: e.Status,
StatusText: StatusText(e.Status), StatusText: StatusText(e.Status),
Description: e.Description,
RequestID: reqID, RequestID: reqID,
CanDebug: e.Status/100 == 4 && (e.DebugURL != nil || reqID != ""), CanDebug: e.Status/100 == 4 && (e.DebugURL != nil || reqID != ""),
DebugURL: e.DebugURL, DebugURL: e.DebugURL,
@ -85,6 +88,7 @@ func (e *HTTPError) ErrorResponse(ctx context.Context, w http.ResponseWriter, r
m := map[string]any{ m := map[string]any{
"canDebug": response.CanDebug, "canDebug": response.CanDebug,
"description": response.Description,
"requestId": response.RequestID, "requestId": response.RequestID,
"status": response.Status, "status": response.Status,
"statusText": response.StatusText, "statusText": response.StatusText,
@ -101,3 +105,9 @@ func (e *HTTPError) ErrorResponse(ctx context.Context, w http.ResponseWriter, r
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
} }
} }
// WithDescription sets the description in the HTTP error.
func (e *HTTPError) WithDescription(description string) *HTTPError {
e.Description = description
return e
}

View file

@ -62,6 +62,11 @@ export const ErrorPage: FC<ErrorPageProps> = ({ data }) => {
{data?.status || 500}{" "} {data?.status || 500}{" "}
{data?.statusText || "Internal Server Error"} {data?.statusText || "Internal Server Error"}
</AlertTitle> </AlertTitle>
{data?.description ? (
<Markdown>{data.description}</Markdown>
) : (
<></>
)}
</Alert> </Alert>
</Box> </Box>
{!!data?.errorMessageFirstParagraph && ( {!!data?.errorMessageFirstParagraph && (

View file

@ -104,6 +104,7 @@ export type ErrorPageData = BasePageData & {
requestId?: string; requestId?: string;
status?: number; status?: number;
statusText?: string; statusText?: string;
description?: string;
errorMessageFirstParagraph?: string; errorMessageFirstParagraph?: string;
policyEvaluationTraces?: PolicyEvaluationTrace[]; policyEvaluationTraces?: PolicyEvaluationTrace[];
}; };