Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adding form_post support #509

Merged
merged 25 commits into from
Nov 9, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
27ea33c
feat: Adding form_post support
ajanthan Oct 9, 2020
54a9d50
refactor: introducing responseMode enum, setting responseMode during …
ajanthan Oct 14, 2020
d359581
Merge branch 'master' into feat-formpost
ajanthan Oct 14, 2020
ec3e000
refactor: Reusing response mode enum in error handling
ajanthan Oct 20, 2020
0ebd04d
fix: bulding authrize error message according to responseMode
ajanthan Oct 21, 2020
f6dea63
refactor: Moving ParseFormPostResponse test helper to internal package
ajanthan Oct 28, 2020
ded62be
refactor: Renaming ResponseModeNone
ajanthan Oct 29, 2020
c3c5ea7
refactor: intoducing function to do the fragment encoding
ajanthan Oct 29, 2020
e92ec55
refactor: Making formPostHTLML Template configurable
ajanthan Oct 29, 2020
56234b4
Apply suggestions from code review
aeneasr Oct 29, 2020
10b2f25
Merge branch 'master' into feat-formpost
aeneasr Oct 29, 2020
d7c0743
Adding special characters test case
ajanthan Oct 30, 2020
b8db770
Adding ability to client to specify which response modes it allows
ajanthan Oct 30, 2020
9ee157e
Validating response_mode for insecure mode
ajanthan Nov 1, 2020
9ed9df8
Adding test cases for none default response modes
ajanthan Nov 1, 2020
8676b8b
Merge branch 'master' into feat-formpost
aeneasr Nov 4, 2020
eee5473
fix: respect request object
aeneasr Nov 6, 2020
69b2208
u
aeneasr Nov 6, 2020
4b29d15
u
aeneasr Nov 6, 2020
fefcdeb
u
aeneasr Nov 6, 2020
a8be9f2
u
aeneasr Nov 6, 2020
da08c73
u
aeneasr Nov 6, 2020
3e6a1bd
u
aeneasr Nov 6, 2020
6b741ef
u
aeneasr Nov 9, 2020
b812014
u
aeneasr Nov 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions authorize_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import (
"encoding/json"
"fmt"
"net/http"

"github.com/pkg/errors"
)

func (f *Fosite) WriteAuthorizeError(rw http.ResponseWriter, ar AuthorizeRequester, err error) {
Expand Down Expand Up @@ -66,7 +64,11 @@ func (f *Fosite) WriteAuthorizeError(rw http.ResponseWriter, ar AuthorizeRequest
query.Add("state", ar.GetState())

var redirectURIString string
if !(len(ar.GetResponseTypes()) == 0 || ar.GetResponseTypes().ExactOne("code")) && !errors.Is(err, ErrUnsupportedResponseType) {
if ar.GetResponseMode() == ResponseModePost {
rw.Header().Add("Content-Type", "text/html;charset=UTF-8")
WriteAuthorizeFormPostResponse(redirectURI.String(), query, GetPostFormHTMLTemplate(*f), rw)
return
} else if ar.GetResponseMode() == ResponseModeFragment {
redirectURIString = redirectURI.String() + "#" + query.Encode()
} else {
for key, values := range redirectURI.Query() {
Expand Down
29 changes: 29 additions & 0 deletions authorize_error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[0]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"code"}))
req.EXPECT().GetResponseMode().Return(ResponseModeQuery).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -106,6 +107,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[0]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"code"}))
req.EXPECT().GetResponseMode().Return(ResponseModeDefault).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -124,6 +126,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[1]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"code"}))
req.EXPECT().GetResponseMode().Return(ResponseModeQuery).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -142,6 +145,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[1]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"foobar"}))
req.EXPECT().GetResponseMode().Return(ResponseModeFragment).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -160,6 +164,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[0]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"token"}))
req.EXPECT().GetResponseMode().Return(ResponseModeFragment).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -178,6 +183,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[1]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"token"}))
req.EXPECT().GetResponseMode().Return(ResponseModeFragment).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -196,6 +202,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[0]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"code", "token"}))
req.EXPECT().GetResponseMode().Return(ResponseModeFragment).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -214,6 +221,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[1]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"code", "token"}))
req.EXPECT().GetResponseMode().Return(ResponseModeFragment).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -233,6 +241,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[1]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"code", "token"}))
req.EXPECT().GetResponseMode().Return(ResponseModeFragment).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -252,6 +261,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[1]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"id_token"}))
req.EXPECT().GetResponseMode().Return(ResponseModeFragment).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -271,6 +281,7 @@ func TestWriteAuthorizeError(t *testing.T) {
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[1]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"token"}))
req.EXPECT().GetResponseMode().Return(ResponseModeFragment).Times(2)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().WriteHeader(http.StatusFound)
},
Expand All @@ -282,6 +293,24 @@ func TestWriteAuthorizeError(t *testing.T) {
assert.Equal(t, "no-cache", header.Get("Pragma"))
},
},
{
debug: true,
err: ErrInvalidRequest.WithDebug("with-debug"),
mock: func(rw *MockResponseWriter, req *MockAuthorizeRequester) {
req.EXPECT().IsRedirectURIValid().Return(true)
req.EXPECT().GetRedirectURI().Return(copyUrl(purls[1]))
req.EXPECT().GetState().Return("foostate")
req.EXPECT().GetResponseTypes().MaxTimes(2).Return(Arguments([]string{"token"}))
req.EXPECT().GetResponseMode().Return(ResponseModePost).Times(1)
rw.EXPECT().Header().Times(3).Return(header)
rw.EXPECT().Write(gomock.Any()).AnyTimes()
},
checkHeader: func(t *testing.T, k int) {
assert.Equal(t, "no-store", header.Get("Cache-Control"))
assert.Equal(t, "no-cache", header.Get("Pragma"))
assert.Equal(t, "text/html;charset=UTF-8", header.Get("Content-Type"))
},
},
} {
t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) {
oauth2 := &Fosite{
Expand Down
49 changes: 49 additions & 0 deletions authorize_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
package fosite

import (
"fmt"
"html/template"
"io"
"net/url"
"regexp"
"strings"
Expand All @@ -30,6 +33,21 @@ import (
"github.com/pkg/errors"
)

var FormPostDefaultTemplate = template.Must(template.New("form_post").Parse(`<html>
<head>
<title>Submit This Form</title>
</head>
<body onload="javascript:document.forms[0].submit()">
<form method="post" action="{{ .RedirURL }}">
{{ range $key,$value := .Parameters }}
{{ range $parameter:= $value}}
<input type="hidden" name="{{$key}}" value="{{$parameter}}"/>
{{end}}
{{ end }}
</form>
</body>
</html>`))

// MatchRedirectURIWithClientRedirectURIs if the given uri is a registered redirect uri. Does not perform
// uri validation.
//
Expand Down Expand Up @@ -182,3 +200,34 @@ func IsLocalhost(redirectURI *url.URL) bool {
hn := redirectURI.Hostname()
return strings.HasSuffix(hn, ".localhost") || hn == "127.0.0.1" || hn == "::1" || hn == "localhost"
}

func WriteAuthorizeFormPostResponse(redirectURL string, parameters url.Values, template *template.Template, rw io.Writer) {
_ = template.Execute(rw, struct {
RedirURL string
Parameters url.Values
}{
RedirURL: redirectURL,
Parameters: parameters,
})
}
func URLSetFragment(source *url.URL, fragment url.Values) {
var f string
for k, v := range fragment {
for _, vv := range v {
if len(f) != 0 {
f += fmt.Sprintf("&%s=%s", k, vv)
} else {
f += fmt.Sprintf("%s=%s", k, vv)
}
}
}
source.Fragment = f
}

func GetPostFormHTMLTemplate(f Fosite) *template.Template {
formPostHTMLTemplate := f.FormPostHTMLTemplate
if formPostHTMLTemplate == nil {
formPostHTMLTemplate = FormPostDefaultTemplate
}
return formPostHTMLTemplate
}
Loading