Skip to content

Commit

Permalink
IAM: Use SessionDatabase in OpenID4VP (#2525)
Browse files Browse the repository at this point in the history
  • Loading branch information
reinkrul authored Oct 5, 2023
1 parent 7b11473 commit 86557dd
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 162 deletions.
24 changes: 12 additions & 12 deletions auth/api/iam/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ import (
"github.com/nuts-foundation/nuts-node/auth"
"github.com/nuts-foundation/nuts-node/auth/log"
"github.com/nuts-foundation/nuts-node/core"
"github.com/nuts-foundation/nuts-node/storage"
"github.com/nuts-foundation/nuts-node/vcr"
"github.com/nuts-foundation/nuts-node/vdr"
"github.com/nuts-foundation/nuts-node/vdr/resolver"
"html/template"
"net/http"
"strings"
"sync"
)

var _ core.Routable = &Wrapper{}
Expand All @@ -49,25 +49,25 @@ var assets embed.FS

// Wrapper handles OAuth2 flows.
type Wrapper struct {
vcr vcr.VCR
vdr vdr.VDR
auth auth.AuthenticationServices
sessions *SessionManager
templates *template.Template
vcr vcr.VCR
vdr vdr.VDR
auth auth.AuthenticationServices
templates *template.Template
storageEngine storage.Engine
}

func New(authInstance auth.AuthenticationServices, vcrInstance vcr.VCR, vdrInstance vdr.VDR) *Wrapper {
func New(authInstance auth.AuthenticationServices, vcrInstance vcr.VCR, vdrInstance vdr.VDR, storageEngine storage.Engine) *Wrapper {
templates := template.New("oauth2 templates")
_, err := templates.ParseFS(assets, "assets/*.html")
if err != nil {
panic(err)
}
return &Wrapper{
sessions: &SessionManager{sessions: new(sync.Map)},
auth: authInstance,
vcr: vcrInstance,
vdr: vdrInstance,
templates: templates,
storageEngine: storageEngine,
auth: authInstance,
vcr: vcrInstance,
vdr: vdrInstance,
templates: templates,
}
}

Expand Down
115 changes: 0 additions & 115 deletions auth/api/iam/authorized_code.go

This file was deleted.

25 changes: 19 additions & 6 deletions auth/api/iam/openid4vp.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
ssi "github.com/nuts-foundation/go-did"
"github.com/nuts-foundation/go-did/did"
Expand All @@ -33,8 +34,11 @@ import (
"net/http"
"net/url"
"strings"
"time"
)

const sessionExpiry = 5 * time.Minute

// createPresentationRequest creates a new Authorization Request as specified by OpenID4VP: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html.
// It is sent by a verifier to a wallet, to request one or more verifiable credentials as verifiable presentation from the wallet.
func (r *Wrapper) sendPresentationRequest(ctx context.Context, response http.ResponseWriter, scope string,
Expand Down Expand Up @@ -145,7 +149,12 @@ func (r *Wrapper) handlePresentationRequest(params map[string]string, session *S
}
session.ServerState["openid4vp_credentials"] = credentialIDs

templateParams.SessionID = r.sessions.Create(*session)
sessionID := uuid.NewString()
err = r.storageEngine.GetSessionDatabase().GetStore(sessionExpiry, session.OwnDID.String(), "session").Put(sessionID, *session)
if err != nil {
return nil, err
}
templateParams.SessionID = sessionID

// TODO: Support multiple languages
buf := new(bytes.Buffer)
Expand All @@ -162,12 +171,16 @@ func (r *Wrapper) handlePresentationRequest(params map[string]string, session *S
// handleAuthConsent handles the authorization consent form submission.
func (r *Wrapper) handlePresentationRequestAccept(c echo.Context) error {
// TODO: Needs authentication?
var session *Session
if sessionID := c.FormValue("sessionID"); sessionID != "" {
session = r.sessions.Get(sessionID)
sessionID := c.FormValue("sessionID")
if sessionID == "" {
return errors.New("missing sessionID parameter")
}
if session == nil {
return errors.New("invalid session")

var session Session
sessionStore := r.storageEngine.GetSessionDatabase().GetStore(sessionExpiry, "openid", session.OwnDID.String(), "session")
err := sessionStore.Get(sessionID, &session)
if err != nil {
return fmt.Errorf("invalid session: %w", err)
}

// TODO: Change to loading from wallet
Expand Down
9 changes: 5 additions & 4 deletions auth/api/iam/openid4vp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
"github.com/nuts-foundation/nuts-node/auth"
"github.com/nuts-foundation/nuts-node/storage"
"github.com/nuts-foundation/nuts-node/vcr"
"github.com/nuts-foundation/nuts-node/vcr/credential"
"github.com/nuts-foundation/nuts-node/vcr/holder"
Expand All @@ -42,7 +43,7 @@ var holderDID = did.MustParseDID("did:web:example.com:holder")
var issuerDID = did.MustParseDID("did:web:example.com:issuer")

func TestWrapper_sendPresentationRequest(t *testing.T) {
instance := New(nil, nil, nil)
instance := New(nil, nil, nil, nil)

redirectURI, _ := url.Parse("https://example.com/redirect")
verifierID, _ := url.Parse("https://example.com/verifier")
Expand Down Expand Up @@ -101,7 +102,7 @@ func TestWrapper_handlePresentationRequest(t *testing.T) {
mockAuth.EXPECT().PresentationDefinitions().Return(peStore)
mockWallet.EXPECT().List(gomock.Any(), holderDID).Return(walletCredentials, nil)
mockVDR.EXPECT().IsOwner(gomock.Any(), holderDID).Return(true, nil)
instance := New(mockAuth, mockVCR, mockVDR)
instance := New(mockAuth, mockVCR, mockVDR, storage.NewTestStorageEngine(t))

params := map[string]string{
"scope": "eOverdracht-overdrachtsbericht",
Expand All @@ -124,7 +125,7 @@ func TestWrapper_handlePresentationRequest(t *testing.T) {
_ = peStore.LoadFromFile("test/presentation_definition_mapping.json")
mockAuth := auth.NewMockAuthenticationServices(ctrl)
mockAuth.EXPECT().PresentationDefinitions().Return(peStore)
instance := New(mockAuth, nil, nil)
instance := New(mockAuth, nil, nil, nil)

params := map[string]string{
"scope": "unsupported",
Expand All @@ -139,7 +140,7 @@ func TestWrapper_handlePresentationRequest(t *testing.T) {
assert.Nil(t, response)
})
t.Run("invalid response_mode", func(t *testing.T) {
instance := New(nil, nil, nil)
instance := New(nil, nil, nil, nil)
params := map[string]string{
"scope": "eOverdracht-overdrachtsbericht",
"response_type": "code",
Expand Down
24 changes: 0 additions & 24 deletions auth/api/iam/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,10 @@
package iam

import (
"github.com/google/uuid"
"github.com/nuts-foundation/go-did/did"
"net/url"
"sync"
)

type SessionManager struct {
sessions *sync.Map
}

func (s *SessionManager) Create(session Session) string {
// TODO: Session expiration
// TODO: Session storage
// TODO: Session pinning and other safety measures (see OAuth2 Threat Model)
id := uuid.NewString()
s.sessions.Store(id, session)
return id
}

func (s *SessionManager) Get(id string) *Session {
session, ok := s.sessions.Load(id)
if !ok {
return nil
}
result := session.(Session)
return &result
}

type Session struct {
ClientID string
Scope string
Expand Down
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func CreateSystem(shutdownCallback context.CancelFunc) *core.System {
system.RegisterRoutes(statusEngine.(core.Routable))
system.RegisterRoutes(metricsEngine.(core.Routable))
system.RegisterRoutes(&authAPIv1.Wrapper{Auth: authInstance, CredentialResolver: credentialInstance})
system.RegisterRoutes(authIAMAPI.New(authInstance, credentialInstance, vdrInstance))
system.RegisterRoutes(authIAMAPI.New(authInstance, credentialInstance, vdrInstance, storageInstance))
system.RegisterRoutes(&authMeansAPI.Wrapper{Auth: authInstance})
system.RegisterRoutes(&didmanAPI.Wrapper{Didman: didmanInstance})

Expand Down

0 comments on commit 86557dd

Please sign in to comment.