Skip to content

Commit

Permalink
filters/auth: add login redirect stub support (#3028)
Browse files Browse the repository at this point in the history
For single page applications user may want to return javascript stub
that stores location hash into localstorage and then redirects
user to the authorization url.

Signed-off-by: Alexander Yastrebov <[email protected]>
  • Loading branch information
AlexanderYastrebov authored Apr 22, 2024
1 parent 0ce11b9 commit 2a5d037
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 6 deletions.
26 changes: 26 additions & 0 deletions docs/reference/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -1655,6 +1660,27 @@ all:
-> "http://localhost:9090";
```

```
single_page_app:
*
-> annotate("oauthGrant.loginRedirectStub", `
<!doctype html>
<html lang="en">
<head>
<title>Redirecting...</title>
<script>
if (window.location.hash !== null) {
localStorage.setItem('original-location-hash', window.location.hash);
}
window.location.replace('{{authCodeURL}}');
</script>
</head>
</html>
`)
-> oauthGrant()
-> "http://localhost:9090";
```

Skipper arguments:

| Argument | Required? | Description |
Expand Down
29 changes: 23 additions & 6 deletions filters/auth/grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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) {
Expand Down
30 changes: 30 additions & 0 deletions filters/auth/grant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/http"
"net/http/cookiejar"
Expand Down Expand Up @@ -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)
-> <shunt>
`, 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))
}

0 comments on commit 2a5d037

Please sign in to comment.