feat(op): always verify code challenge when available (#721)

Finally the RFC Best Current Practice for OAuth 2.0 Security has been approved.

According to the RFC:

> Authorization servers MUST support PKCE [RFC7636].
> 
> If a client sends a valid PKCE code_challenge parameter in the authorization request, the authorization server MUST enforce the correct usage of code_verifier at the token endpoint.

Isn’t it time we strengthen PKCE support a bit more?

This PR updates the logic so that PKCE is always verified, even when the Auth Method is not "none".
This commit is contained in:
Ayato 2025-03-25 01:00:04 +09:00 committed by GitHub
parent 7096406e71
commit c51628ea27
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 45 additions and 15 deletions

View file

@ -7,6 +7,7 @@ import (
"log/slog"
"net/http"
"os"
"strconv"
"strings"
"sync/atomic"
"time"
@ -34,6 +35,14 @@ func main() {
scopes := strings.Split(os.Getenv("SCOPES"), " ")
responseMode := os.Getenv("RESPONSE_MODE")
var pkce bool
if pkceEnv, ok := os.LookupEnv("PKCE"); ok {
var err error
pkce, err = strconv.ParseBool(pkceEnv)
if err != nil {
logrus.Fatalf("error parsing PKCE %s", err.Error())
}
}
redirectURI := fmt.Sprintf("http://localhost:%v%v", port, callbackPath)
cookieHandler := httphelper.NewCookieHandler(key, key, httphelper.WithUnsecure())
@ -64,6 +73,9 @@ func main() {
if keyPath != "" {
options = append(options, rp.WithJWTProfile(rp.SignerFromKeyPath(keyPath)))
}
if pkce {
options = append(options, rp.WithPKCE(cookieHandler))
}
// One can add a logger to the context,
// pre-defining log attributes as required.

View file

@ -25,5 +25,5 @@
<button type="submit">Login</button>
</form>
</body>
</html>`
{{- end }}
</html>
{{- end }}

View file

@ -18,7 +18,7 @@ const (
// CustomClaim is an example for how to return custom claims with this library
CustomClaim = "custom_claim"
// CustomScopeImpersonatePrefix is an example scope prefix for passing user id to impersonate using token exchage
// CustomScopeImpersonatePrefix is an example scope prefix for passing user id to impersonate using token exchange
CustomScopeImpersonatePrefix = "custom_scope:impersonate:"
)
@ -143,6 +143,14 @@ func MaxAgeToInternal(maxAge *uint) *time.Duration {
}
func authRequestToInternal(authReq *oidc.AuthRequest, userID string) *AuthRequest {
var codeChallenge *OIDCCodeChallenge
if authReq.CodeChallenge != "" {
codeChallenge = &OIDCCodeChallenge{
Challenge: authReq.CodeChallenge,
Method: string(authReq.CodeChallengeMethod),
}
}
return &AuthRequest{
CreationDate: time.Now(),
ApplicationID: authReq.ClientID,
@ -157,10 +165,7 @@ func authRequestToInternal(authReq *oidc.AuthRequest, userID string) *AuthReques
ResponseType: authReq.ResponseType,
ResponseMode: authReq.ResponseMode,
Nonce: authReq.Nonce,
CodeChallenge: &OIDCCodeChallenge{
Challenge: authReq.CodeChallenge,
Method: string(authReq.CodeChallengeMethod),
},
CodeChallenge: codeChallenge,
}
}