(w http.ResponseWriter, r *http.Request)
| 151 | } |
| 152 | |
| 153 | func (s *state) handleToken(w http.ResponseWriter, r *http.Request) { |
| 154 | r.ParseForm() |
| 155 | grantType := r.Form.Get("grant_type") |
| 156 | code := r.Form.Get("code") |
| 157 | codeVerifier := r.Form.Get("code_verifier") |
| 158 | // Ignore redirect_uri; it is not required in 2.1. |
| 159 | // https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-13.html#redirect-uri-in-token-request |
| 160 | |
| 161 | if grantType != "authorization_code" { |
| 162 | http.Error(w, "unsupported_grant_type", http.StatusBadRequest) |
| 163 | return |
| 164 | } |
| 165 | s.mu.Lock() |
| 166 | authCodeInfo, ok := s.authCodes[code] |
| 167 | if !ok { |
| 168 | http.Error(w, "invalid_grant", http.StatusBadRequest) |
| 169 | return |
| 170 | } |
| 171 | delete(s.authCodes, code) |
| 172 | s.mu.Unlock() |
| 173 | |
| 174 | // PKCE verification. |
| 175 | hasher := sha256.New() |
| 176 | hasher.Write([]byte(codeVerifier)) |
| 177 | calculatedChallenge := base64.RawURLEncoding.EncodeToString(hasher.Sum(nil)) |
| 178 | if calculatedChallenge != authCodeInfo.codeChallenge { |
| 179 | http.Error(w, "invalid_grant", http.StatusBadRequest) |
| 180 | return |
| 181 | } |
| 182 | |
| 183 | // Issue JWT. |
| 184 | now := time.Now() |
| 185 | claims := jwt.MapClaims{ |
| 186 | "iss": getBaseURL(r), |
| 187 | "sub": "fake-user-id", |
| 188 | "aud": "fake-client-id", |
| 189 | "exp": now.Add(tokenExpiry).Unix(), |
| 190 | "iat": now.Unix(), |
| 191 | } |
| 192 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) |
| 193 | accessToken, err := token.SignedString(jwtSigningKey) |
| 194 | if err != nil { |
| 195 | http.Error(w, "server_error", http.StatusInternalServerError) |
| 196 | return |
| 197 | } |
| 198 | |
| 199 | tokenResponse := map[string]any{ |
| 200 | "access_token": accessToken, |
| 201 | "token_type": "Bearer", |
| 202 | "expires_in": int(tokenExpiry.Seconds()), |
| 203 | } |
| 204 | var buf bytes.Buffer |
| 205 | if err := json.NewEncoder(&buf).Encode(tokenResponse); err != nil { |
| 206 | http.Error(w, err.Error(), http.StatusInternalServerError) |
| 207 | return |
| 208 | } |
| 209 | w.Header().Set("Content-Type", "application/json") |
| 210 | w.Write(buf.Bytes()) |
nothing calls this directly
no test coverage detected