OAuth2GetCode emulates a user clicking "allow" on the IDP page. When doing unit tests, it's easier to skip this step sometimes. It does make an actual request to the IDP, so it should be equivalent to doing this "manually" with actual requests.
(rawAuthURL string, doRequest func(req *http.Request) (*http.Response, error))
| 122 | // request to the IDP, so it should be equivalent to doing this "manually" with |
| 123 | // actual requests. |
| 124 | func OAuth2GetCode(rawAuthURL string, doRequest func(req *http.Request) (*http.Response, error)) (string, error) { |
| 125 | authURL, err := url.Parse(rawAuthURL) |
| 126 | if err != nil { |
| 127 | return "", xerrors.Errorf("failed to parse auth URL: %w", err) |
| 128 | } |
| 129 | |
| 130 | r, err := http.NewRequestWithContext(context.Background(), http.MethodGet, rawAuthURL, nil) |
| 131 | if err != nil { |
| 132 | return "", xerrors.Errorf("failed to create auth request: %w", err) |
| 133 | } |
| 134 | |
| 135 | resp, err := doRequest(r) |
| 136 | if err != nil { |
| 137 | return "", xerrors.Errorf("request: %w", err) |
| 138 | } |
| 139 | defer resp.Body.Close() |
| 140 | |
| 141 | // Accept both 302 (Found) and 307 (Temporary Redirect) as valid OAuth2 redirects |
| 142 | if resp.StatusCode != http.StatusFound && resp.StatusCode != http.StatusTemporaryRedirect { |
| 143 | return "", codersdk.ReadBodyAsError(resp) |
| 144 | } |
| 145 | |
| 146 | to := resp.Header.Get("Location") |
| 147 | if to == "" { |
| 148 | return "", xerrors.Errorf("expected redirect location") |
| 149 | } |
| 150 | |
| 151 | toURL, err := url.Parse(to) |
| 152 | if err != nil { |
| 153 | return "", xerrors.Errorf("failed to parse redirect location: %w", err) |
| 154 | } |
| 155 | |
| 156 | code := toURL.Query().Get("code") |
| 157 | if code == "" { |
| 158 | return "", xerrors.Errorf("expected code in redirect location") |
| 159 | } |
| 160 | |
| 161 | state := authURL.Query().Get("state") |
| 162 | newState := toURL.Query().Get("state") |
| 163 | if newState != state { |
| 164 | return "", xerrors.Errorf("expected state %q, got %q", state, newState) |
| 165 | } |
| 166 | return code, nil |
| 167 | } |