diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 808ef5f..4ce2966 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -126,12 +126,17 @@ func oauthResource(server string, meta map[string]any) string { func registerPublicClient(endpoint string, redirectURIs, grantTypes, responseTypes []string) (map[string]any, error) { body := map[string]any{ "client_name": "life-ustc-cli", - "redirect_uris": redirectURIs, + "application_type": "native", "token_endpoint_auth_method": "none", "grant_types": grantTypes, - "response_types": responseTypes, "scope": oauthScope, } + if len(redirectURIs) > 0 { + body["redirect_uris"] = redirectURIs + } + if len(responseTypes) > 0 { + body["response_types"] = responseTypes + } data, _ := json.Marshal(body) client := &http.Client{Timeout: 15 * time.Second} resp, err := client.Post(endpoint, "application/json", bytes.NewReader(data)) diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index 75af9f7..641547d 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -3,6 +3,7 @@ package auth import ( "crypto/rand" "crypto/rsa" + "encoding/json" "net" "net/http" "net/http/httptest" @@ -15,6 +16,75 @@ import ( "github.com/go-jose/go-jose/v4/jwt" ) +func TestRegisterPublicClientUsesNativeApplicationType(t *testing.T) { + requests := make(chan map[string]any, 1) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var body map[string]any + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + requests <- body + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + _, _ = w.Write([]byte(`{"client_id":"client-1"}`)) + })) + t.Cleanup(server.Close) + + _, err := registerPublicClient( + server.URL, + []string{"http://127.0.0.1:46289/callback"}, + []string{"authorization_code", "refresh_token"}, + []string{"code"}, + ) + if err != nil { + t.Fatal(err) + } + body := <-requests + if body["application_type"] != "native" { + t.Fatalf("application_type = %#v, want native", body["application_type"]) + } + redirectURIs, ok := body["redirect_uris"].([]any) + if !ok || len(redirectURIs) != 1 || redirectURIs[0] != "http://127.0.0.1:46289/callback" { + t.Fatalf("redirect_uris = %#v", body["redirect_uris"]) + } +} + +func TestRegisterPublicClientOmitsUnusedDeviceRedirectMetadata(t *testing.T) { + requests := make(chan map[string]any, 1) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var body map[string]any + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + requests <- body + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"client_id":"device-client"}`)) + })) + t.Cleanup(server.Close) + + _, err := registerPublicClient( + server.URL, + nil, + []string{"urn:ietf:params:oauth:grant-type:device_code", "refresh_token"}, + nil, + ) + if err != nil { + t.Fatal(err) + } + body := <-requests + if body["application_type"] != "native" { + t.Fatalf("application_type = %#v, want native", body["application_type"]) + } + if _, ok := body["redirect_uris"]; ok { + t.Fatalf("redirect_uris should be omitted, got %#v", body["redirect_uris"]) + } + if _, ok := body["response_types"]; ok { + t.Fatalf("response_types should be omitted, got %#v", body["response_types"]) + } +} + func TestOAuthCallbackHandlerDeliversOnlyFirstRequest(t *testing.T) { results := make(chan callbackResult, 1) handler := oauthCallbackHandler(results) diff --git a/internal/auth/device.go b/internal/auth/device.go index 2a1e844..b37edd2 100644 --- a/internal/auth/device.go +++ b/internal/auth/device.go @@ -38,9 +38,9 @@ func LoginDeviceCode(server string) (*config.Credential, error) { // Register client clientInfo, err := registerPublicClient( regEndpoint, - []string{"http://localhost/callback"}, + nil, []string{"urn:ietf:params:oauth:grant-type:device_code", "refresh_token"}, - []string{"code"}, + nil, ) if err != nil { return nil, err