Skip to content

Commit 31e97ad

Browse files
authored
Add Check PCKE Support (#563)
Refer To [Issue 473](#473) Changes: - Add check for PKCE support via server metadata and return an error if not supported.
1 parent 920e5a3 commit 31e97ad

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

oauthex/auth_meta.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ func GetAuthServerMeta(ctx context.Context, issuerURL string, c *http.Client) (*
139139
// Security violation; don't keep trying.
140140
return nil, fmt.Errorf("metadata issuer %q does not match issuer URL %q", asm.Issuer, issuerURL)
141141
}
142+
143+
if len(asm.CodeChallengeMethodsSupported) == 0 {
144+
return nil, fmt.Errorf("authorization server at %s does not implement PKCE", issuerURL)
145+
}
146+
142147
return asm, nil
143148
}
144149
errs = append(errs, err)

oauthex/auth_meta_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
77
package oauthex
88

99
import (
10+
"context"
1011
"encoding/json"
12+
"net/http"
13+
"net/http/httptest"
14+
"net/url"
1115
"os"
1216
"path/filepath"
17+
"strings"
1318
"testing"
1419
)
1520

@@ -28,3 +33,88 @@ func TestAuthMetaParse(t *testing.T) {
2833
t.Errorf("got %q, want %q", g, w)
2934
}
3035
}
36+
37+
func TestGetAuthServerMetaPKCESupport(t *testing.T) {
38+
ctx := context.Background()
39+
tests := []struct {
40+
name string
41+
hasPKCESupport bool
42+
wantError string
43+
}{
44+
{
45+
name: "server_with_pkce_support",
46+
hasPKCESupport: true,
47+
},
48+
{
49+
name: "server_without_pkce_support",
50+
hasPKCESupport: false,
51+
wantError: "does not implement PKCE",
52+
},
53+
}
54+
55+
for _, tt := range tests {
56+
t.Run(tt.name, func(t *testing.T) {
57+
// Start a fake OAuth 2.1 auth server
58+
wrapper := http.NewServeMux()
59+
wrapper.HandleFunc("/.well-known/oauth-authorization-server", func(w http.ResponseWriter, r *http.Request) {
60+
u, _ := url.Parse("https://" + r.Host)
61+
issuer := "https://localhost:" + u.Port()
62+
metadata := AuthServerMeta{
63+
Issuer: issuer,
64+
AuthorizationEndpoint: issuer + "/authorize",
65+
TokenEndpoint: issuer + "/token",
66+
RegistrationEndpoint: issuer + "/register",
67+
JWKSURI: issuer + "/.well-known/jwks.json",
68+
ScopesSupported: []string{"openid", "profile", "email"},
69+
ResponseTypesSupported: []string{"code"},
70+
GrantTypesSupported: []string{"authorization_code"},
71+
TokenEndpointAuthMethodsSupported: []string{"none"},
72+
}
73+
74+
// Add PKCE support based on test case
75+
if tt.hasPKCESupport {
76+
metadata.CodeChallengeMethodsSupported = []string{"S256"}
77+
}
78+
// If hasPKCESupport is false, CodeChallengeMethodsSupported remains empty
79+
80+
w.Header().Set("Content-Type", "application/json")
81+
json.NewEncoder(w).Encode(metadata)
82+
})
83+
ts := httptest.NewTLSServer(wrapper)
84+
defer ts.Close()
85+
86+
// The fake server sets issuer to https://localhost:<port>, so compute that issuer.
87+
u, _ := url.Parse(ts.URL)
88+
issuer := "https://localhost:" + u.Port()
89+
90+
// The fake server presents a cert for example.com; set ServerName accordingly.
91+
httpClient := ts.Client()
92+
if tr, ok := httpClient.Transport.(*http.Transport); ok {
93+
clone := tr.Clone()
94+
clone.TLSClientConfig.ServerName = "example.com"
95+
httpClient.Transport = clone
96+
}
97+
98+
meta, err := GetAuthServerMeta(ctx, issuer, httpClient)
99+
if tt.wantError != "" {
100+
if err == nil {
101+
t.Fatal("wanted error but got none")
102+
}
103+
if !strings.Contains(err.Error(), tt.wantError) {
104+
t.Errorf("wanted error to contain %q, but got: %v", tt.wantError, err)
105+
}
106+
} else {
107+
if err != nil {
108+
t.Fatalf("unwanted error: %v", err)
109+
}
110+
if meta == nil {
111+
t.Fatal("wanted metadata but got nil")
112+
}
113+
// Verify PKCE support is present
114+
if len(meta.CodeChallengeMethodsSupported) == 0 {
115+
t.Error("wanted PKCE support but CodeChallengeMethodsSupported is empty")
116+
}
117+
}
118+
})
119+
}
120+
}

0 commit comments

Comments
 (0)