Skip to content
This repository has been archived by the owner on Apr 11, 2023. It is now read-only.

test: gnap - interact redirect steps #205

Merged
merged 1 commit into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion pkg/restapi/gnap/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (o *Operation) authRequestHandler(w http.ResponseWriter, req *http.Request)
func (o *Operation) interactHandler(w http.ResponseWriter, req *http.Request) {
// TODO validate txn_id
// redirect to UI
http.Redirect(w, req, o.uiEndpoint, http.StatusFound)
http.Redirect(w, req, o.uiEndpoint+"/sign-up", http.StatusFound)
}

func (o *Operation) authContinueHandler(w http.ResponseWriter, req *http.Request) {
Expand Down
6 changes: 3 additions & 3 deletions test/bdd/fixtures/auth-rest/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ services:
- AUTH_REST_DATABASE_TYPE=mongodb
- AUTH_REST_DATABASE_URL=mongodb://mongodb.example.com:27017
- AUTH_REST_DATABASE_PREFIX=authrest_
- AUTH_REST_OIDC_CALLBACK=https://localhost:8070/oauth2/callback # https://github.com/trustbloc/auth/issues/13
- AUTH_REST_OIDC_CALLBACK=https://auth.trustbloc.local:8070/oauth2/callback # https://github.com/trustbloc/auth/issues/13
- AUTH_REST_OIDC_PROVIDERS_CONFIG=/etc/oidc-config/providers.yaml
- AUTH_REST_SDS_DOCS_URL=https://TODO.docs.sds.org # onboard user: https://github.com/trustbloc/auth/issues/38
- AUTH_REST_SDS_OPSKEYS_URL=https://TODO.keys.sds.org
Expand Down Expand Up @@ -58,8 +58,8 @@ services:
environment:
- DSN=mysql://authresthydra:authresthydra-secret-pw@tcp(mysql:3306)/authresthydra?max_conns=20&max_idle_conns=4
- URLS_SELF_ISSUER=https://localhost:4444/
- URLS_CONSENT=https://localhost:8070/hydra/consent
- URLS_LOGIN=https://localhost:8070/hydra/login
- URLS_CONSENT=https://auth.trustbloc.local:8070/hydra/consent
- URLS_LOGIN=https://auth.trustbloc.local:8070/hydra/login
- SECRETS_SYSTEM=testSecretsSystem
- OIDC_SUBJECT_TYPES_SUPPORTED=public
- OIDC_SUBJECT_TYPE_PAIRWISE_SALT=testSecretsSystem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ hydra clients create \
--response-types code,id_token \
--scope openid,profile,email \
--skip-tls-verify \
--callbacks https://localhost:8070/oauth2/callback
--callbacks https://auth.trustbloc.local:8070/oauth2/callback
# TODO it would be great to check the exit status of the hydra command
# https://github.com/trustbloc/auth/issues/67
echo "Finished creating oidc client!"
Expand All @@ -32,7 +32,7 @@ hydra clients create \
--response-types code,id_token \
--scope openid,profile,email \
--skip-tls-verify \
--callbacks https://localhost:8070/oauth2/callback
--callbacks https://auth.trustbloc.local:8070/oauth2/callback
# TODO it would be great to check the exit status of the hydra command
# https://github.com/trustbloc/auth/issues/67
echo "Finished creating oidc client for gnap flow!"
1 change: 1 addition & 0 deletions test/bdd/mock/loginconsent/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module mock_server
go 1.17

require (
github.com/google/uuid v1.1.2
github.com/gorilla/mux v1.8.0
github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210807121559-b41545a4f1e8
github.com/hyperledger/aries-framework-go/spi v0.0.0-20210807121559-b41545a4f1e8
Expand Down
61 changes: 52 additions & 9 deletions test/bdd/mock/loginconsent/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/http"
"time"

"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/hyperledger/aries-framework-go/spi/storage"
"github.com/ory/hydra-client-go/client"
Expand All @@ -21,6 +22,8 @@ import (
const (
loginChallengeCookieName = "bdd_test_cookie_login_challenge"
consentChallengeCookieName = "bdd_test_cookie_consent_challenge"
skipConsentCookieName = "skipConsent"
skipConsentTrue = "true"
)

func newServer(c *config) *server {
Expand All @@ -45,6 +48,7 @@ func newServer(c *config) *server {

mockRouter := s.router.PathPrefix("/mock").Subrouter()
mockRouter.HandleFunc("/login", s.loginHandler).Methods(http.MethodGet)
mockRouter.HandleFunc("/login", s.postLoginHandler).Methods(http.MethodPost)
mockRouter.HandleFunc("/authn", s.userAuthNHandler).Methods(http.MethodPost)
mockRouter.HandleFunc("/consent", s.consentHandler).Methods(http.MethodGet)
mockRouter.HandleFunc("/authz", s.userAuthZHandler).Methods(http.MethodPost)
Expand Down Expand Up @@ -102,7 +106,7 @@ func (s *server) loginHandler(w http.ResponseWriter, r *http.Request) {
Value: challenge,
})

_, err := w.Write([]byte("mock login UI"))
_, err := w.Write([]byte("<!DOCTYPE html>\n<html>\n<body>\n\n<p>mock login UI</p>\n\n<form action=\"/mock/login\" method=\"post\" id=\"form1\">\n</form>\n\n<button type=\"submit\" form=\"form1\">login</button>\n\n</body>\n</html>\n"))
if err != nil {
logger.Errorf("failed to write imaginary UI: %s", err.Error())

Expand All @@ -112,19 +116,41 @@ func (s *server) loginHandler(w http.ResponseWriter, r *http.Request) {
logger.Infof("rendered mock login UI in response to request %s", r.URL.String())
}

func (s *server) postLoginHandler(w http.ResponseWriter, r *http.Request) {
logger.Infof("success case %s", r.URL.String())

http.SetCookie(w, &http.Cookie{
Name: skipConsentCookieName,
Value: skipConsentTrue,
})

s.completeLogin(w, r, &AuthConfigRequest{
Sub: uuid.New().String(),
})
}

func (s *server) userAuthNHandler(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie(loginChallengeCookieName)
request := &AuthConfigRequest{}

err := json.NewDecoder(r.Body).Decode(request)
if err != nil {
logger.Errorf("failed to fetch cookie %s: %s", loginChallengeCookieName, err.Error())
logger.Errorf("failed to decode auth request: %s", err.Error())

return
}

request := &AuthConfigRequest{}
http.SetCookie(w, &http.Cookie{
Name: skipConsentCookieName,
Value: "",
})

err = json.NewDecoder(r.Body).Decode(request)
s.completeLogin(w, r, request)
}

func (s *server) completeLogin(w http.ResponseWriter, r *http.Request, request *AuthConfigRequest) {
cookie, err := r.Cookie(loginChallengeCookieName)
if err != nil {
logger.Errorf("failed to decode auth request: %s", err.Error())
logger.Errorf("failed to fetch cookie %s: %s", loginChallengeCookieName, err.Error())

return
}
Expand Down Expand Up @@ -190,6 +216,19 @@ func (s *server) userAuthNHandler(w http.ResponseWriter, r *http.Request) {
func (s *server) consentHandler(w http.ResponseWriter, r *http.Request) {
logger.Infof("handling request: %s", r.URL.String())

skipConsent, err := r.Cookie(skipConsentCookieName)
if err != nil {
logger.Errorf("failed to fetch cookie %s: %s", skipConsentCookieName, err.Error())

return
}

logger.Infof("consent skip value %s", skipConsent.Value)

if skipConsent.Value == skipConsentTrue {
s.completeConsent(w, r, &ConsentConfigRequest{UserClaims: &UserClaims{}}, r.URL.Query().Get("consent_challenge"))
}

challenge := r.URL.Query().Get("consent_challenge")
if challenge == "" {
logger.Errorf("missing consent_challenge")
Expand All @@ -202,7 +241,7 @@ func (s *server) consentHandler(w http.ResponseWriter, r *http.Request) {
Value: challenge,
})

_, err := w.Write([]byte("mock consent UI"))
_, err = w.Write([]byte("mock consent UI"))
if err != nil {
logger.Errorf("failed to write imaginary UI: %s", err.Error())

Expand All @@ -229,11 +268,15 @@ func (s *server) userAuthZHandler(w http.ResponseWriter, r *http.Request) {
return
}

s.completeConsent(w, r, request, cookie.Value)
}

func (s *server) completeConsent(w http.ResponseWriter, r *http.Request, request *ConsentConfigRequest, consentChallenge string) {
params := admin.NewGetConsentRequestParams()

params.SetContext(r.Context())
params.SetHTTPClient(s.httpClient)
params.SetConsentChallenge(cookie.Value)
params.SetConsentChallenge(consentChallenge)

consent, err := s.hydra.GetConsentRequest(params)
if err != nil {
Expand Down Expand Up @@ -262,7 +305,7 @@ func (s *server) userAuthZHandler(w http.ResponseWriter, r *http.Request) {
reject := admin.NewRejectConsentRequestParams()
reject.SetContext(r.Context())
reject.SetHTTPClient(s.httpClient)
reject.SetConsentChallenge(cookie.Value)
reject.SetConsentChallenge(consentChallenge)

rejected, err := s.hydra.RejectConsentRequest(reject)
if err != nil {
Expand Down
71 changes: 66 additions & 5 deletions test/bdd/pkg/gnap/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"crypto/rand"
"fmt"
"net/http"
"net/http/cookiejar"
"strings"

"github.com/cucumber/godog"
"github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk"
Expand All @@ -24,13 +26,20 @@ import (
const (
authServerURL = "https://auth.trustbloc.local:8070"
expectedInteractURL = authServerURL + "/gnap/interact"

oidcProviderSelectorURL = authServerURL + "/oauth2/login"
oidcCallbackURLURL = authServerURL + "/oauth2/callback"
authServerSignUpURL = authServerURL + "/ui/sign-up"

mockOIDCProviderName = "mockbank1" // providers.yaml
)

type Steps struct {
ctx *bddctx.BDDContext
gnapClient *as.Client
pubKeyJWK jwk.JWK
authResp *gnap.AuthResponse
browser *http.Client
}

func NewSteps(ctx *bddctx.BDDContext) *Steps {
Expand Down Expand Up @@ -97,7 +106,7 @@ func (s *Steps) txnRequest() error {

if authResp.Interact.Redirect != expectedInteractURL {
return fmt.Errorf(
"invalid interact url: expected %s got %s",
"invalid interact url: expected=%s actual=%s",
expectedInteractURL, authResp.Interact.Redirect,
)
}
Expand All @@ -108,13 +117,51 @@ func (s *Steps) txnRequest() error {
}

func (s *Steps) interactRedirect() error {
// TODO get interact url
// initialise the browser
s.initBrowser()

// redirect to interact url
response, err := s.browser.Get(s.authResp.Interact.Redirect)
if err != nil {
return err
}

defer func() {
closeErr := response.Body.Close()
if closeErr != nil {
fmt.Printf("WARNING - failed to close http response body: %s\n", closeErr.Error())
}
}()

// validate the redirect url
if response.Request.URL.String() != authServerSignUpURL {
return fmt.Errorf(
"invalid ui redirect url: expected=%s actual=%s", authServerSignUpURL, response.Request.URL.String(),
)
}

// select provider
request := fmt.Sprintf("%s?provider=%s", oidcProviderSelectorURL, mockOIDCProviderName)

// TODO use browser to redirect
fmt.Println(request)

result, err := s.browser.Get(fmt.Sprintf("%s?provider=%s", oidcProviderSelectorURL, mockOIDCProviderName))
if err != nil {
return fmt.Errorf("failed to redirect to OIDC provider url %s: %w", request, err)
}

// TODO select provider
// login to third party oidc
loginResp, err := s.browser.Post(result.Request.URL.String(), "", nil)
if err != nil {
return err
}

// TODO login to third party oidc
if !strings.HasPrefix(loginResp.Request.URL.String(), oidcCallbackURLURL) {
return fmt.Errorf(
"invalid oidc callbackURL prefix expected=%s actual=%s",
oidcCallbackURLURL, loginResp.Request.URL.String(),
)
}

// TODO get the redirect back

Expand All @@ -130,3 +177,17 @@ func (s *Steps) continueRequest() error {

return nil
}

func (s *Steps) initBrowser() error {
jar, err := cookiejar.New(nil)
if err != nil {
return fmt.Errorf("failed to init cookie jar: %w", err)
}

s.browser = &http.Client{
Jar: jar,
Transport: &http.Transport{TLSClientConfig: s.ctx.TLSConfig()},
}

return nil
}
2 changes: 1 addition & 1 deletion test/bdd/pkg/login/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
)

const (
AUTH_HOST = "https://localhost:8070"
AUTH_HOST = "https://auth.trustbloc.local:8070"
hubAuthHydraAdminURL = "https://localhost:4445"
hubAuthOIDCProviderURL = "https://localhost:4444/"
hubAuthOIDCProviderSelectionURL = AUTH_HOST + "/ui"
Expand Down