From 484182a0f8bbc5961c68b1c40f2e0ffb7453b914 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Mon, 25 Oct 2021 11:57:26 +0200 Subject: [PATCH] handle response mode --- example/internal/mock/storage.go | 5 +++++ pkg/oidc/authorization.go | 3 +++ pkg/oidc/types.go | 2 ++ pkg/op/auth_request.go | 37 +++++++++++++++++++++++++++----- pkg/op/error.go | 17 ++++++--------- 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/example/internal/mock/storage.go b/example/internal/mock/storage.go index 214ba54..867cfa0 100644 --- a/example/internal/mock/storage.go +++ b/example/internal/mock/storage.go @@ -32,6 +32,7 @@ func NewAuthStorage() op.Storage { type AuthRequest struct { ID string ResponseType oidc.ResponseType + ResponseMode oidc.ResponseMode RedirectURI string Nonce string ClientID string @@ -87,6 +88,10 @@ func (a *AuthRequest) GetResponseType() oidc.ResponseType { return a.ResponseType } +func (a *AuthRequest) GetResponseMode() oidc.ResponseMode { + return a.ResponseMode +} + func (a *AuthRequest) GetScopes() []string { return []string{ "openid", diff --git a/pkg/oidc/authorization.go b/pkg/oidc/authorization.go index 1e8795f..e6cfe58 100644 --- a/pkg/oidc/authorization.go +++ b/pkg/oidc/authorization.go @@ -42,6 +42,9 @@ const ( DisplayTouch Display = "touch" DisplayWAP Display = "wap" + ResponseModeQuery ResponseMode = "query" + ResponseModeFragment ResponseMode = "fragment" + //PromptNone (`none`) disallows the Authorization Server to display any authentication or consent user interface pages. //An error (login_required, interaction_required, ...) will be returned if the user is not already authenticated or consent is needed PromptNone = "none" diff --git a/pkg/oidc/types.go b/pkg/oidc/types.go index 0bee12b..b6a75f4 100644 --- a/pkg/oidc/types.go +++ b/pkg/oidc/types.go @@ -67,6 +67,8 @@ type Prompt SpaceDelimitedArray type ResponseType string +type ResponseMode string + func (s SpaceDelimitedArray) Encode() string { return strings.Join(s, " ") } diff --git a/pkg/op/auth_request.go b/pkg/op/auth_request.go index 79dc97f..932db21 100644 --- a/pkg/op/auth_request.go +++ b/pkg/op/auth_request.go @@ -26,6 +26,7 @@ type AuthRequest interface { GetNonce() string GetRedirectURI() string GetResponseType() oidc.ResponseType + GetResponseMode() oidc.ResponseMode GetScopes() []string GetState() string GetSubject() string @@ -413,9 +414,17 @@ func AuthResponseCode(w http.ResponseWriter, r *http.Request, authReq AuthReques AuthRequestError(w, r, authReq, err, authorizer.Encoder()) return } - callback := fmt.Sprintf("%s?code=%s", authReq.GetRedirectURI(), code) - if authReq.GetState() != "" { - callback = callback + "&state=" + authReq.GetState() + codeResponse := struct { + Code string + State string + }{ + Code: code, + State: authReq.GetState(), + } + callback, err := AuthResponseURL(authReq.GetRedirectURI(), authReq.GetResponseType(), authReq.GetResponseMode(), &codeResponse, authorizer.Encoder()) + if err != nil { + AuthRequestError(w, r, authReq, err, authorizer.Encoder()) + return } http.Redirect(w, r, callback, http.StatusFound) } @@ -428,12 +437,11 @@ func AuthResponseToken(w http.ResponseWriter, r *http.Request, authReq AuthReque AuthRequestError(w, r, authReq, err, authorizer.Encoder()) return } - params, err := httphelper.URLEncodeResponse(resp, authorizer.Encoder()) + callback, err := AuthResponseURL(authReq.GetRedirectURI(), authReq.GetResponseType(), authReq.GetResponseMode(), resp, authorizer.Encoder()) if err != nil { AuthRequestError(w, r, authReq, err, authorizer.Encoder()) return } - callback := fmt.Sprintf("%s#%s", authReq.GetRedirectURI(), params) http.Redirect(w, r, callback, http.StatusFound) } @@ -453,3 +461,22 @@ func CreateAuthRequestCode(ctx context.Context, authReq AuthRequest, storage Sto func BuildAuthRequestCode(authReq AuthRequest, crypto Crypto) (string, error) { return crypto.Encrypt(authReq.GetID()) } + +//AuthResponseURL encodes the authorization response (successful and error) and sets it as query or fragment values +//depending on the response_mode and response_type +func AuthResponseURL(redirectURI string, responseType oidc.ResponseType, responseMode oidc.ResponseMode, response interface{}, encoder httphelper.Encoder) (string, error) { + params, err := httphelper.URLEncodeResponse(response, encoder) + if err != nil { + return "", err + } + if responseMode == oidc.ResponseModeQuery { + return redirectURI + "?" + params, nil + } + if responseMode == oidc.ResponseModeFragment { + return redirectURI + "#" + params, nil + } + if responseType == "" || responseType == oidc.ResponseTypeCode { + return redirectURI + "?" + params, nil + } + return redirectURI + "#" + params, nil +} diff --git a/pkg/op/error.go b/pkg/op/error.go index cc8267b..dc1c87f 100644 --- a/pkg/op/error.go +++ b/pkg/op/error.go @@ -19,23 +19,20 @@ func AuthRequestError(w http.ResponseWriter, r *http.Request, authReq ErrAuthReq return } e := oidc.DefaultToServerError(err, err.Error()) //TODO: desc? - e.State = authReq.GetState() - if authReq.GetRedirectURI() == "" || e.IsRedirectDisabled() { + if authReq == nil || authReq.GetRedirectURI() == "" || e.IsRedirectDisabled() { http.Error(w, e.Description, http.StatusBadRequest) return } - params, err := httphelper.URLEncodeResponse(e, encoder) + e.State = authReq.GetState() + var responseMode oidc.ResponseMode + if rm, ok := authReq.(interface{ GetResponseMode() oidc.ResponseMode }); ok { + responseMode = rm.GetResponseMode() + } + url, err := AuthResponseURL(authReq.GetRedirectURI(), authReq.GetResponseType(), responseMode, e, encoder) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } - url := authReq.GetRedirectURI() - responseType := authReq.GetResponseType() - if responseType == "" || responseType == oidc.ResponseTypeCode { - url += "?" + params - } else { - url += "#" + params - } http.Redirect(w, r, url, http.StatusFound) }