getAccessTokenForDeviceCode verifies the device code and returns an access token if the request is approved
(w http.ResponseWriter, r *http.Request, values url.Values)
| 162 | |
| 163 | // getAccessTokenForDeviceCode verifies the device code and returns an access token if the request is approved |
| 164 | func (a *Authenticator) getAccessTokenForDeviceCode(w http.ResponseWriter, r *http.Request, values url.Values) { |
| 165 | deviceCode := values.Get("device_code") |
| 166 | if deviceCode == "" { |
| 167 | http.Error(w, "device_code is required", http.StatusBadRequest) |
| 168 | return |
| 169 | } |
| 170 | grantType := values.Get("grant_type") |
| 171 | if grantType != deviceCodeGrantType { |
| 172 | http.Error(w, "invalid grant_type", http.StatusBadRequest) |
| 173 | return |
| 174 | } |
| 175 | responseVersion := values.Get("token_response_version") |
| 176 | |
| 177 | authCode, err := a.admin.DB.FindDeviceAuthCodeByDeviceCode(r.Context(), deviceCode) |
| 178 | if err != nil { |
| 179 | if errors.Is(err, database.ErrNotFound) { |
| 180 | http.Error(w, fmt.Sprintf("no such device code: %s found", deviceCode), http.StatusBadRequest) |
| 181 | return |
| 182 | } |
| 183 | internalServerError(w, fmt.Errorf("failed to get auth code for deviceCode: %s, %w", deviceCode, err)) |
| 184 | return |
| 185 | } |
| 186 | clientID := values.Get("client_id") |
| 187 | if clientID != authCode.ClientID { |
| 188 | http.Error(w, "invalid client_id", http.StatusBadRequest) |
| 189 | return |
| 190 | } |
| 191 | |
| 192 | if authCode.Expiry.Before(time.Now()) { |
| 193 | http.Error(w, "expired_token", http.StatusUnauthorized) |
| 194 | return |
| 195 | } |
| 196 | if authCode.ApprovalState == database.DeviceAuthCodeStateRejected { |
| 197 | err = a.admin.DB.DeleteDeviceAuthCode(r.Context(), deviceCode) |
| 198 | if err != nil { |
| 199 | internalServerError(w, fmt.Errorf("failed to clean up rejected code: %s, %w", deviceCode, err)) |
| 200 | return |
| 201 | } |
| 202 | http.Error(w, "rejected", http.StatusUnauthorized) |
| 203 | return |
| 204 | } |
| 205 | if authCode.ApprovalState == database.DeviceAuthCodeStatePending { |
| 206 | http.Error(w, "authorization_pending", http.StatusUnauthorized) |
| 207 | return |
| 208 | } |
| 209 | if authCode.ApprovalState != database.DeviceAuthCodeStateApproved || authCode.UserID == nil { |
| 210 | internalServerError(w, fmt.Errorf("inconsistent state, %w", errors.New("server error"))) |
| 211 | return |
| 212 | } |
| 213 | // TODO handle too many requests |
| 214 | |
| 215 | authToken, err := a.admin.IssueUserAuthToken(r.Context(), *authCode.UserID, authCode.ClientID, "", nil, nil, false) |
| 216 | if err != nil { |
| 217 | internalServerError(w, fmt.Errorf("failed to issue access token, %w", err)) |
| 218 | return |
| 219 | } |
| 220 | |
| 221 | err = a.admin.DB.DeleteDeviceAuthCode(r.Context(), deviceCode) |
no test coverage detected