Skip to content

Commit 1183cc4

Browse files
committed
chore: implement device flow in idp fake
1 parent d67c9d1 commit 1183cc4

File tree

1 file changed

+47
-0
lines changed
  • coderd/coderdtest/oidctest

1 file changed

+47
-0
lines changed

coderd/coderdtest/oidctest/idp.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"errors"
1111
"fmt"
1212
"io"
13+
"mime"
1314
"net"
1415
"net/http"
1516
"net/http/cookiejar"
@@ -34,6 +35,7 @@ import (
3435
"cdr.dev/slog/sloggers/slogtest"
3536
"github.com/coder/coder/v2/coderd"
3637
"github.com/coder/coder/v2/coderd/externalauth"
38+
"github.com/coder/coder/v2/coderd/httpapi"
3739
"github.com/coder/coder/v2/coderd/promoauth"
3840
"github.com/coder/coder/v2/coderd/util/syncmap"
3941
"github.com/coder/coder/v2/codersdk"
@@ -226,6 +228,7 @@ const (
226228
authorizePath = "/oauth2/authorize"
227229
keysPath = "/oauth2/keys"
228230
userInfoPath = "/oauth2/userinfo"
231+
deviceAuth = "/login/device/code"
229232
)
230233

231234
func NewFakeIDP(t testing.TB, opts ...FakeIDPOpt) *FakeIDP {
@@ -784,6 +787,8 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
784787
f.refreshTokensUsed.Store(refreshToken, true)
785788
// Always invalidate the refresh token after it is used.
786789
f.refreshTokens.Delete(refreshToken)
790+
case "urn:ietf:params:oauth:grant-type:device_code":
791+
// Device flow
787792
default:
788793
t.Errorf("unexpected grant_type %q", values.Get("grant_type"))
789794
http.Error(rw, "invalid grant_type", http.StatusBadRequest)
@@ -886,6 +891,48 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
886891
_ = json.NewEncoder(rw).Encode(set)
887892
}))
888893

894+
mux.Handle(deviceAuth, http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
895+
p := httpapi.NewQueryParamParser()
896+
p.Required("client_id")
897+
p.Required("scopes")
898+
clientID := p.String(r.URL.Query(), "", "client_id")
899+
_ = p.String(r.URL.Query(), "", "scopes")
900+
if len(p.Errors) > 0 {
901+
httpapi.Write(r.Context(), rw, http.StatusBadRequest, codersdk.Response{
902+
Message: fmt.Sprintf("Invalid query params"),
903+
Validations: p.Errors,
904+
})
905+
return
906+
}
907+
908+
if clientID != f.clientID {
909+
httpapi.Write(r.Context(), rw, http.StatusBadRequest, codersdk.Response{
910+
Message: fmt.Sprintf("Invalid client id"),
911+
})
912+
return
913+
}
914+
915+
if mediaType, _, _ := mime.ParseMediaType(r.Header.Get("Accept")); mediaType == "application/json" {
916+
httpapi.Write(r.Context(), rw, http.StatusOK, map[string]any{
917+
"device_code": uuid.NewString(),
918+
"user_code": "1234",
919+
"verification_uri": "",
920+
"expires_in": 900,
921+
"interval": 0,
922+
})
923+
return
924+
}
925+
926+
// By default, GitHub form encodes these.
927+
_, _ = fmt.Fprint(rw, url.Values{
928+
"device_code": {uuid.NewString()},
929+
"user_code": {"1234"},
930+
"verification_uri": {""},
931+
"expires_in": {"900"},
932+
"interval": {"0"},
933+
})
934+
}))
935+
889936
mux.NotFound(func(rw http.ResponseWriter, r *http.Request) {
890937
f.logger.Error(r.Context(), "http call not found", slog.F("path", r.URL.Path))
891938
t.Errorf("unexpected request to IDP at path %q. Not supported", r.URL.Path)

0 commit comments

Comments
 (0)