Skip to content

Commit

Permalink
e2e: Adds e2e tests for authorize code flow
Browse files Browse the repository at this point in the history
  • Loading branch information
arekkas authored and aeneasr committed May 19, 2018
1 parent eb0c3e6 commit 0a9ae28
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 62 deletions.
56 changes: 0 additions & 56 deletions .circleci/.travis.yml

This file was deleted.

1 change: 1 addition & 0 deletions .circleci/config.yml
Expand Up @@ -35,6 +35,7 @@ jobs:
- run: go get -u github.com/mattn/goveralls golang.org/x/tools/cmd/cover github.com/ory/go-acc
- run: dep ensure -vendor-only
- run: go install github.com/ory/hydra
- run: go install github.com/ory/hydra/test/mock-lcp
- run: go-acc -o coverage.txt ./...
- run: go test -race -short $(go list ./... | grep -v cmd)
- run: ./scripts/test-e2e.sh
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -16,3 +16,4 @@ coverage.*
Dockerfile-plugin-*
plugin-*.so
hydra-docker-bin
cookies.txt
4 changes: 4 additions & 0 deletions consent/helper.go
Expand Up @@ -23,6 +23,8 @@ package consent
import (
"net/http"

"fmt"

"github.com/gorilla/sessions"
"github.com/ory/fosite"
"github.com/ory/go-convenience/mapx"
Expand Down Expand Up @@ -63,6 +65,7 @@ func createCsrfSession(w http.ResponseWriter, r *http.Request, store sessions.St
session.Values["csrf"] = csrf
session.Options.HttpOnly = true
session.Options.Secure = secure
fmt.Printf("\n\n\n\nCREATE COOKIE %+v\n\n\n\n", session.Values)

if err := session.Save(r, w); err != nil {
return errors.WithStack(err)
Expand All @@ -75,6 +78,7 @@ func validateCsrfSession(r *http.Request, store sessions.Store, name, expectedCS
if cookie, err := store.Get(r, name); err != nil {
return errors.WithStack(fosite.ErrRequestForbidden.WithDebug("CSRF session cookie could not be decoded"))
} else if csrf, err := mapx.GetString(cookie.Values, "csrf"); err != nil {
fmt.Printf("\n\n\n\nGOT COOKIE %+v\n\n\n\nHEADER: %+v", cookie.Values, r.Header)
return errors.WithStack(fosite.ErrRequestForbidden.WithDebug("No CSRF value available in the session cookie"))
} else if csrf != expectedCSRF {
return errors.WithStack(fosite.ErrRequestForbidden.WithDebug("The CSRF value from the token does not match the CSRF value from the data store"))
Expand Down
87 changes: 81 additions & 6 deletions scripts/test-e2e.sh
Expand Up @@ -4,14 +4,89 @@ set -euo pipefail

cd "$( dirname "${BASH_SOURCE[0]}" )/.."

DATABASE_URL=memory hydra serve --dangerous-force-http --disable-telemetry &
while ! echo exit | nc 127.0.0.1 4444; do sleep 1; done
killall hydra || true
killall mock-lcp || true
killall mock-cb || true

export HYDRA_URL=http://localhost:4444/
export HYDRA_URL=http://127.0.0.1:4444/
export OAUTH2_CLIENT_ID=foobar
export OAUTH2_CLIENT_SECRET=bazbar
export OAUTH2_ISSUER_URL=http://127.0.0.1:4444/
export LOG_LEVEL=debug
export REDIRECT_URL=http://127.0.0.1:4445/callback
export AUTH2_SCOPE=openid,offline

go install .
go install ./test/mock-client
go install ./test/mock-lcp
go install ./test/mock-cb

DATABASE_URL=memory \
OAUTH2_CONSENT_URL=http://127.0.0.1:3000/consent \
OAUTH2_LOGIN_URL=http://127.0.0.1:3000/login \
OAUTH2_ERROR_URL=http://127.0.0.1:3000/error \
OAUTH2_SHARE_ERROR_DEBUG=true \
hydra serve --dangerous-force-http --disable-telemetry &

PORT=3000 mock-lcp &

PORT=4445 mock-cb &

while ! echo exit | nc 127.0.0.1 4444; do sleep 1; done
while ! echo exit | nc 127.0.0.1 4445; do sleep 1; done
while ! echo exit | nc 127.0.0.1 3000; do sleep 1; done


hydra clients create \
--endpoint http://127.0.0.1:4444 \
--id $OAUTH2_CLIENT_ID \
--secret $OAUTH2_CLIENT_SECRET \
--response-types token,code,id_token \
--grant-types refresh_token,authorization_code,client_credentials \
--scope openid,offline \
--callbacks http://127.0.0.1:4445/callback

hydra clients create --id $OAUTH2_CLIENT_ID --secret $OAUTH2_CLIENT_SECRET -g client_credentials
token=$(hydra token client)
hydra token introspect $token
hydra clients delete foobar

hydra token introspect "$token"

## Authenticate but do not remember user
cookie=$(OAUTH2_EXTRA="&mockLogin=accept&mockConsent=accept" \
mock-client)
export AUTH_COOKIE=$cookie

## Must fail because prompt=none but no session was remembered
if OAUTH2_EXTRA="&mockLogin=accept&mockConsent=accept&prompt=none" \
mock-client; then
echo "should have failed"
exit 1
fi

# Authenticate and remember login (but not consent)
cookie=$(OAUTH2_EXTRA="&mockLogin=accept&mockConsent=accept&rememberLogin=yes" \
mock-client)
export AUTH_COOKIE=$cookie

## Must fail because prompt=none but consent was not yet stored
if OAUTH2_EXTRA="&mockLogin=accept&mockConsent=accept&prompt=none" \
mock-client; then
echo "should have failed"
exit 1
fi

# Remember consent
cookie=$(OAUTH2_EXTRA="&mockLogin=accept&mockConsent=accept&rememberConsent=yes" \
mock-client)

## Prompt none should work now because cookie was set
OAUTH2_EXTRA="&mockLogin=accept&mockConsent=accept&prompt=none" \
mock-client

hydra clients delete $OAUTH2_CLIENT_ID

kill %1
kill %2
kill %3
exit 0

sleep 5
67 changes: 67 additions & 0 deletions test/mock-cb/main.go
@@ -0,0 +1,67 @@
/*
* Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Aeneas Rekkas <aeneas+oss@aeneas.io>
* @Copyright 2017-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
* @license Apache-2.0
*/

package main

import (
"log"
"net/http"
"os"

"context"
"strings"

"golang.org/x/oauth2"
)

func callback(rw http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("error") != "" {
http.Error(rw, "error happened in callback: "+r.URL.Query().Get("error")+" "+r.URL.Query().Get("error_description")+" "+r.URL.Query().Get("error_debug"), http.StatusInternalServerError)
return
}

code := r.URL.Query().Get("code")
conf := oauth2.Config{
ClientID: os.Getenv("OAUTH2_CLIENT_ID"),
ClientSecret: os.Getenv("OAUTH2_CLIENT_SECRET"),
Endpoint: oauth2.Endpoint{
AuthURL: strings.TrimRight(os.Getenv("HYDRA_URL"), "/") + "/oauth2/auth",
TokenURL: strings.TrimRight(os.Getenv("HYDRA_URL"), "/") + "/oauth2/token",
},
RedirectURL: os.Getenv("REDIRECT_URL"),
}

token, err := conf.Exchange(context.Background(), code)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}

rw.Write([]byte(`access_token=` + token.AccessToken))
}

func main() {
http.HandleFunc("/callback", callback)
port := "4445"
if os.Getenv("PORT") != "" {
port = os.Getenv("PORT")
}
log.Fatal(http.ListenAndServe(":"+port, nil))
}
90 changes: 90 additions & 0 deletions test/mock-client/main.go
@@ -0,0 +1,90 @@
/*
* Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Aeneas Rekkas <aeneas+oss@aeneas.io>
* @Copyright 2017-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
* @license Apache-2.0
*/

package main

import (
"log"
"net/http"
"os"

"io/ioutil"
"net/http/cookiejar"
"strings"

"fmt"
"net/url"

"golang.org/x/oauth2"
)

func main() {
conf := oauth2.Config{
ClientID: os.Getenv("OAUTH2_CLIENT_ID"),
ClientSecret: os.Getenv("OAUTH2_CLIENT_SECRET"),
Endpoint: oauth2.Endpoint{
AuthURL: strings.TrimRight(os.Getenv("HYDRA_URL"), "/") + "/oauth2/auth",
TokenURL: strings.TrimRight(os.Getenv("HYDRA_URL"), "/") + "/oauth2/token",
},
Scopes: strings.Split(os.Getenv("OAUTH2_SCOPE"), ","),
RedirectURL: os.Getenv("REDIRECT_URL"),
}
au := conf.AuthCodeURL("some-stupid-state-foo") + os.Getenv("OAUTH2_EXTRA")
c, err := cookiejar.New(&cookiejar.Options{})
if err != nil {
log.Fatalf("Unable to create cookie jar: %s", err)
}

u, _ := url.Parse("http://127.0.0.1")
if os.Getenv("AUTH_COOKIE") != "" {
c.SetCookies(u, []*http.Cookie{{Name: "oauth2_authentication_session", Value: os.Getenv("AUTH_COOKIE")}})
}

resp, err := (&http.Client{
Jar: c,
// Hack to fix cookie across domains
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) > 0 && req.Header.Get("cookie") == "" {
req.Header.Set("Cookie", via[len(via)-1].Header.Get("Cookie"))
}

return nil
},
}).Get(au)
if err != nil {
log.Fatalf("Unable to make request: %s", err)
}
defer resp.Body.Close()

out, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Unable to read body: %s", err)
}

if resp.StatusCode != http.StatusOK {
log.Fatalf("Got status code %d and body %s", resp.StatusCode, out)
}

for _, c := range c.Cookies(u) {
if c.Name == "oauth2_authentication_session" {
fmt.Print(c.Value)
}
}
}

0 comments on commit 0a9ae28

Please sign in to comment.