diff --git a/docs/reference/filters.md b/docs/reference/filters.md index cf702a2784..d83bb2d088 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1643,6 +1643,11 @@ you pass the `-enable-oauth2-grant-flow` flag. The filter may be used with the [grantClaimsQuery](#grantclaimsquery) filter to perform authz and access control. +The filter also supports javascript login redirect stub that can be used e.g. to store location hash. +To enable the stub, add preceding [annotate](#annotate) filter with `oauthGrant.loginRedirectStub` key and +HTML content that will be served to the client instead of `307 Temporary Redirect` to the authorization URL. +The filter will replace `{{authCodeURL}}` placeholder in the content with the actual authorization URL. + See the [tutorial](../tutorials/auth.md#oauth2-authorization-grant-flow) for step-by-step instructions. @@ -1655,6 +1660,27 @@ all: -> "http://localhost:9090"; ``` +``` +single_page_app: + * + -> annotate("oauthGrant.loginRedirectStub", ` + + + + Redirecting... + + + + `) + -> oauthGrant() + -> "http://localhost:9090"; +``` + Skipper arguments: | Argument | Required? | Description | diff --git a/filters/auth/grant.go b/filters/auth/grant.go index 577f385940..43cac8c4ef 100644 --- a/filters/auth/grant.go +++ b/filters/auth/grant.go @@ -4,10 +4,14 @@ import ( "context" "errors" "fmt" + "io" "net/http" + "strconv" + "strings" "time" "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/annotate" "golang.org/x/oauth2" ) @@ -84,12 +88,25 @@ func loginRedirectWithOverride(ctx filters.FilterContext, config *OAuthConfig, o return } - ctx.Serve(&http.Response{ - StatusCode: http.StatusTemporaryRedirect, - Header: http.Header{ - "Location": []string{authConfig.AuthCodeURL(state, config.GetAuthURLParameters(redirect)...)}, - }, - }) + authCodeURL := authConfig.AuthCodeURL(state, config.GetAuthURLParameters(redirect)...) + + if lrs, ok := annotate.GetAnnotations(ctx)["oauthGrant.loginRedirectStub"]; ok { + lrs = strings.ReplaceAll(lrs, "{{authCodeURL}}", authCodeURL) + ctx.Serve(&http.Response{ + StatusCode: http.StatusOK, + Header: http.Header{ + "Content-Length": []string{strconv.Itoa(len(lrs))}, + }, + Body: io.NopCloser(strings.NewReader(lrs)), + }) + } else { + ctx.Serve(&http.Response{ + StatusCode: http.StatusTemporaryRedirect, + Header: http.Header{ + "Location": []string{authCodeURL}, + }, + }) + } } func (f *grantFilter) refreshToken(token *oauth2.Token, req *http.Request) (*oauth2.Token, error) { diff --git a/filters/auth/grant_test.go b/filters/auth/grant_test.go index 1d7bda2276..d0d10a4e8e 100644 --- a/filters/auth/grant_test.go +++ b/filters/auth/grant_test.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "encoding/json" + "fmt" "net" "net/http" "net/http/cookiejar" @@ -1000,3 +1001,32 @@ func TestGrantInsecure(t *testing.T) { } } } + +func TestGrantLoginRedirectStub(t *testing.T) { + provider := newGrantTestAuthServer(testToken, testAccessCode) + defer provider.Close() + + tokeninfo := newGrantTestTokeninfo(testToken, "") + defer tokeninfo.Close() + + config := newGrantTestConfig(tokeninfo.URL, provider.URL) + + const stubContent = "stub content" + + routes := eskip.MustParse(fmt.Sprintf(`* + -> annotate("oauthGrant.loginRedirectStub", "%s") + -> oauthGrant() + -> status(204) + -> + `, stubContent)) + + proxy, client := newAuthProxy(t, config, routes) + defer proxy.Close() + + rsp, body, err := client.GetBody(proxy.URL + "/test") + require.NoError(t, err) + + assert.Equal(t, rsp.StatusCode, http.StatusOK) + assert.Equal(t, int64(len(stubContent)), rsp.ContentLength) + assert.Equal(t, stubContent, string(body)) +}