-
Notifications
You must be signed in to change notification settings - Fork 1
methods for the OIDC auth flow #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
d4fc09a
3d99bf5
5690382
8539a8f
8ddbe48
4f524e8
52b16da
444b0a0
e34532c
bf253de
efa19e9
733e929
b332cd3
0ced945
42e4875
aaedfb7
8d29ab2
be7eae8
39cd3d7
ce2e1a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -467,6 +467,28 @@ func (c *Client) streamingPostDNClient(ctx context.Context, reqType string, valu | |
| return sc, nil | ||
| } | ||
|
|
||
| func (c *Client) handleBody(resp *http.Response) ([]byte, error) { | ||
| respBody, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to read the response body: %s", err) | ||
| } | ||
|
|
||
| switch resp.StatusCode { | ||
| case http.StatusOK: | ||
| return respBody, nil | ||
| case http.StatusUnauthorized: | ||
| return nil, ErrInvalidCredentials | ||
| default: | ||
| var errors struct { | ||
| Errors message.APIErrors | ||
| } | ||
| if err := json.Unmarshal(respBody, &errors); err != nil { | ||
| return nil, fmt.Errorf("dnclient endpoint returned bad status code '%d', body: %s", resp.StatusCode, respBody) | ||
|
||
| } | ||
| return nil, errors.Errors.ToError() | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| // postDNClient wraps and signs the given dnclientRequestWrapper message, and makes the API call. | ||
| // On success, it returns the response message body. On error, the error is returned. | ||
| func (c *Client) postDNClient(ctx context.Context, reqType string, value []byte, hostID string, counter uint, privkey keys.PrivateKey) ([]byte, error) { | ||
|
|
@@ -489,25 +511,7 @@ func (c *Client) postDNClient(ctx context.Context, reqType string, value []byte, | |
| } | ||
| defer resp.Body.Close() | ||
|
|
||
| respBody, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to read the response body: %s", err) | ||
| } | ||
|
|
||
| switch resp.StatusCode { | ||
| case http.StatusOK: | ||
| return respBody, nil | ||
| case http.StatusUnauthorized: | ||
| return nil, ErrInvalidCredentials | ||
| default: | ||
| var errors struct { | ||
| Errors message.APIErrors | ||
| } | ||
| if err := json.Unmarshal(respBody, &errors); err != nil { | ||
| return nil, fmt.Errorf("dnclient endpoint returned bad status code '%d', body: %s", resp.StatusCode, respBody) | ||
| } | ||
| return nil, errors.Errors.ToError() | ||
| } | ||
| return c.handleBody(resp) | ||
| } | ||
|
|
||
| // StreamController is used for interacting with streaming requests to the API. | ||
|
|
@@ -581,3 +585,80 @@ func nonce() []byte { | |
| } | ||
| return nonce | ||
| } | ||
|
|
||
| func (c *Client) GetOidcPollCode(ctx context.Context, logger logrus.FieldLogger) (string, error) { | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| logger.WithFields(logrus.Fields{"server": c.dnServer}).Debug("Making GetOidcPollCode request to API") | ||
|
|
||
| enrollURL, err := url.JoinPath(c.dnServer, message.PreAuthEndpoint) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| req, err := http.NewRequestWithContext(ctx, "POST", enrollURL, nil) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| resp, err := c.client.Do(req) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
| defer resp.Body.Close() | ||
|
|
||
| // Log the request ID returned from the server | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| reqID := resp.Header.Get("X-Request-ID") | ||
| l := logger.WithFields(logrus.Fields{"statusCode": resp.StatusCode, "reqID": reqID}) | ||
| b, err := c.handleBody(resp) | ||
| if err != nil { | ||
| l.Error(err) //todo I don't like erroring and also logging? | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return "", err | ||
| } | ||
|
|
||
| // Decode the response | ||
| r := message.PreAuthResponse{} | ||
| if err = json.Unmarshal(b, &r); err != nil { | ||
| return "", &APIError{e: fmt.Errorf("error decoding JSON response: %s\nbody: %s", err, b), ReqID: reqID} | ||
| } | ||
|
|
||
| return r.PollToken, nil | ||
| } | ||
|
|
||
| func (c *Client) DoOidcPoll(ctx context.Context, logger logrus.FieldLogger, pollCode string) (*message.EnduserAuthPollResponse, error) { | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| logger.WithFields(logrus.Fields{"server": c.dnServer}).Debug("Making DoOidcPoll request to API") | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| enrollURL, err := url.JoinPath(c.dnServer, message.EnduserAuthPoll) | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| req, err := http.NewRequestWithContext(ctx, "GET", enrollURL, nil) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| q := req.URL.Query() | ||
| q.Add("token", pollCode) | ||
| req.URL.RawQuery = q.Encode() | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| resp, err := c.client.Do(req) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| defer resp.Body.Close() | ||
|
|
||
| // Log the request ID returned from the server | ||
| reqID := resp.Header.Get("X-Request-ID") | ||
| l := logger.WithFields(logrus.Fields{"statusCode": resp.StatusCode, "reqID": reqID}) | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| b, err := c.handleBody(resp) | ||
| if err != nil { | ||
| l.Error(err) //todo I don't like erroring and also logging? | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return nil, err | ||
| } | ||
|
|
||
| // Decode the response | ||
| r := message.EnduserAuthPollResponse{} | ||
| if err = json.Unmarshal(b, &r); err != nil { | ||
| return nil, &APIError{e: fmt.Errorf("error decoding JSON response: %s\nbody: %s", err, b), ReqID: reqID} | ||
| } | ||
|
|
||
| return &r, nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -67,6 +67,14 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) { | |
| s.handlerEnroll(w, r) | ||
| case message.EndpointV1: | ||
| s.handlerDNClient(w, r) | ||
| case message.PreAuthEndpoint: | ||
| expected := s.expectedRequests[0] | ||
| s.expectedRequests = s.expectedRequests[1:] | ||
| res := expected.dncRequestResponse | ||
| w.WriteHeader(res.statusCode) | ||
| _, _ = w.Write(res.response(message.RequestWrapper{})) | ||
|
||
| case message.EnduserAuthPoll: | ||
| s.handlerDoOidcPoll(w, r) | ||
| default: | ||
| s.errors = append(s.errors, fmt.Errorf("invalid request path %s", r.URL.Path)) | ||
| http.NotFound(w, r) | ||
|
|
@@ -152,6 +160,29 @@ func (s *Server) SetP256Pubkey(p256PubkeyPEM []byte) error { | |
| return nil | ||
| } | ||
|
|
||
| func (s *Server) handlerDoOidcPoll(w http.ResponseWriter, r *http.Request) { | ||
| // Get the test case to validate | ||
| expected := s.expectedRequests[0] | ||
| s.expectedRequests = s.expectedRequests[1:] | ||
| if !expected.dnclientAPI { | ||
| s.errors = append(s.errors, fmt.Errorf("unexpected dnclient API request - expected enrollment request")) | ||
| http.Error(w, "unexpected dnclient API request", http.StatusInternalServerError) | ||
| return | ||
| } | ||
| res := expected.dncRequestResponse | ||
|
|
||
| token := r.URL.Query()["token"] | ||
| if len(token) == 0 { | ||
| s.errors = append(s.errors, fmt.Errorf("missing token")) | ||
| http.Error(w, "missing token", http.StatusBadRequest) | ||
| return | ||
| } | ||
|
|
||
| // return the associated response | ||
| w.WriteHeader(res.statusCode) | ||
| w.Write(res.response(message.RequestWrapper{})) | ||
| } | ||
|
|
||
| func (s *Server) handlerDNClient(w http.ResponseWriter, r *http.Request) { | ||
| // Get the test case to validate | ||
| expected := s.expectedRequests[0] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -218,3 +218,21 @@ func (nc *NetworkCurve) UnmarshalJSON(b []byte) error { | |
|
|
||
| return nil | ||
| } | ||
|
|
||
| const PreAuthEndpoint = "/v1/enduser-auth/preauth" | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| type PreAuthResponse struct { | ||
| PollToken string `json:"pollToken"` | ||
| } | ||
|
|
||
| const EnduserAuthPoll = "/v1/enduser-auth/poll" | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| const EnduserAuthPollStatusNotStarted = "NOT_STARTED" | ||
|
||
| const EnduserAuthPollStatusStarted = "OIDC_STARTED" | ||
| const EnduserAuthPollStatusSuccess = "SUCCESS" | ||
|
|
||
| type EnduserAuthPollResponse struct { | ||
| Status string `json:"status"` | ||
| LoginUrl string `json:"loginUrl"` | ||
JackDoan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| EnrollmentCode string `json:"enrollmentCode"` | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.