Skip to content

Commit df2455e

Browse files
feat: add client credentials test
1 parent 1562840 commit df2455e

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

cmd/server/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77

88
"github.com/speakeasy-api/speakeasy-api-test-service/internal/acceptHeaders"
9+
"github.com/speakeasy-api/speakeasy-api-test-service/internal/clientcredentials"
910
"github.com/speakeasy-api/speakeasy-api-test-service/internal/errors"
1011
"github.com/speakeasy-api/speakeasy-api-test-service/internal/eventstreams"
1112
"github.com/speakeasy-api/speakeasy-api-test-service/internal/pagination"
@@ -47,6 +48,8 @@ func main() {
4748
r.HandleFunc("/eventstreams/rich", eventstreams.HandleEventStreamRich).Methods(http.MethodPost)
4849
r.HandleFunc("/eventstreams/chat", eventstreams.HandleEventStreamChat).Methods(http.MethodPost)
4950
r.HandleFunc("/eventstreams/differentdataschemas", eventstreams.HandleEventStreamDifferentDataSchemas).Methods(http.MethodPost)
51+
r.HandleFunc("/clientcredentials/token", clientcredentials.HandleTokenRequest).Methods(http.MethodPost)
52+
r.HandleFunc("/clientcredentials/authenticatedrequest", clientcredentials.HandleAuthenticatedRequest).Methods(http.MethodPost)
5053

5154
bind := ":8080"
5255
if bindArg != nil {

internal/clientcredentials/service.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package clientcredentials
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"slices"
7+
"strings"
8+
"sync"
9+
)
10+
11+
var state = sync.Map{}
12+
13+
const (
14+
firstAccessToken = "super-duper-access-token"
15+
secondAccessToken = "second-super-duper-access-token"
16+
)
17+
18+
func HandleTokenRequest(w http.ResponseWriter, r *http.Request) {
19+
err := r.ParseForm()
20+
if err != nil {
21+
http.Error(w, "invalid_request", http.StatusBadRequest)
22+
return
23+
}
24+
25+
grant_type := r.Form.Get("grant_type")
26+
if grant_type != "client_credentials" {
27+
http.Error(w, "invalid_grant", http.StatusBadRequest)
28+
return
29+
}
30+
31+
clientID := r.Form.Get("client_id")
32+
clientSecret := r.Form.Get("client_secret")
33+
if clientID == "" || clientSecret == "" {
34+
http.Error(w, "invalid_request", http.StatusBadRequest)
35+
return
36+
}
37+
if clientID != "speakeasy-sdks" || !strings.HasPrefix(clientSecret, "supersecret-") {
38+
http.Error(w, "invalid_client", http.StatusUnauthorized)
39+
return
40+
}
41+
42+
scopes := strings.Split(r.Form.Get("scope"), " ")
43+
if len(scopes) == 0 {
44+
http.Error(w, "invalid_scope", http.StatusBadRequest)
45+
return
46+
}
47+
48+
if !slices.Contains(scopes, "read") || !slices.Contains(scopes, "write") {
49+
http.Error(w, "invalid_scope", http.StatusBadRequest)
50+
return
51+
}
52+
53+
accessToken := firstAccessToken
54+
55+
_, ok := state.Load(clientSecret)
56+
if !ok {
57+
state.Store(clientSecret, true)
58+
} else {
59+
accessToken = secondAccessToken
60+
}
61+
62+
w.Header().Set("Content-Type", "application/json")
63+
64+
type tokenResponse struct {
65+
AccessToken string `json:"access_token"`
66+
TokenType string `json:"token_type"`
67+
ExpiresIn int `json:"expires_in"`
68+
}
69+
70+
response := tokenResponse{
71+
AccessToken: accessToken,
72+
TokenType: "Bearer",
73+
ExpiresIn: 0,
74+
}
75+
76+
if err := json.NewEncoder(w).Encode(response); err != nil {
77+
http.Error(w, "server_error", http.StatusInternalServerError)
78+
return
79+
}
80+
}
81+
82+
func HandleAuthenticatedRequest(w http.ResponseWriter, r *http.Request) {
83+
accessToken := r.Header.Get("Authorization")
84+
if accessToken == "" {
85+
http.Error(w, "invalid_request", http.StatusBadRequest)
86+
return
87+
}
88+
89+
accessToken = strings.TrimPrefix(accessToken, "Bearer ")
90+
if accessToken != firstAccessToken && accessToken != secondAccessToken {
91+
http.Error(w, "invalid_token", http.StatusUnauthorized)
92+
return
93+
}
94+
95+
w.WriteHeader(http.StatusOK)
96+
}

0 commit comments

Comments
 (0)