diff --git a/api/api.yaml b/api/api.yaml index 2c33ff416..407b2b260 100644 --- a/api/api.yaml +++ b/api/api.yaml @@ -973,6 +973,11 @@ paths: name: uuid path: github.com/google/uuid example: 8edd8112-c415-11ed-b036-debe37e1cbd6 + - in: query + name: issuer + schema: + type: string + responses: '200': description: A json to generate a QR code @@ -1262,7 +1267,6 @@ paths: - Links parameters: - $ref: '#/components/parameters/pathIdentifier' - - $ref: '#/components/parameters/sessionID' - $ref: '#/components/parameters/linkID' requestBody: required: true @@ -2202,9 +2206,6 @@ components: universalLink: type: string example: https://wallet.privado.id#request_uri=url - sessionID: - type: string - example: ab5d5dbf-aaaa-bbbb-b983-f48afea64e05 linkDetail: $ref: '#/components/schemas/LinkSimple' diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index ff5a7cc91..983649571 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -287,7 +287,6 @@ type CredentialLinkQrCodeResponse struct { Issuer IssuerDescription `json:"issuer"` LinkDetail LinkSimple `json:"linkDetail"` QrCodeRaw string `json:"qrCodeRaw"` - SessionID string `json:"sessionID"` UniversalLink string `json:"universalLink"` } @@ -686,9 +685,6 @@ type CreateLinkQrCodeCallbackTextBody = string // CreateLinkQrCodeCallbackParams defines parameters for CreateLinkQrCodeCallback. type CreateLinkQrCodeCallbackParams struct { - // SessionID Session ID e.g: 89d298fa-15a6-4a1d-ab13-d1069467eedd - SessionID SessionID `form:"sessionID" json:"sessionID"` - // LinkID Session ID e.g: 89d298fa-15a6-4a1d-ab13-d1069467eedd LinkID LinkID `form:"linkID" json:"linkID"` } @@ -762,7 +758,8 @@ type GetStateTransactionsParamsSort string // GetQrFromStoreParams defines parameters for GetQrFromStore. type GetQrFromStoreParams struct { - Id *uuid.UUID `form:"id,omitempty" json:"id,omitempty"` + Id *uuid.UUID `form:"id,omitempty" json:"id,omitempty"` + Issuer *string `form:"issuer,omitempty" json:"issuer,omitempty"` } // AuthQRCodeParams defines parameters for AuthQRCode. @@ -1778,21 +1775,6 @@ func (siw *ServerInterfaceWrapper) CreateLinkQrCodeCallback(w http.ResponseWrite // Parameter object where we will unmarshal all parameters from the context var params CreateLinkQrCodeCallbackParams - // ------------- Required query parameter "sessionID" ------------- - - if paramValue := r.URL.Query().Get("sessionID"); paramValue != "" { - - } else { - siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "sessionID"}) - return - } - - err = runtime.BindQueryParameter("form", true, true, "sessionID", r.URL.Query(), ¶ms.SessionID) - if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "sessionID", Err: err}) - return - } - // ------------- Required query parameter "linkID" ------------- if paramValue := r.URL.Query().Get("linkID"); paramValue != "" { @@ -2534,6 +2516,14 @@ func (siw *ServerInterfaceWrapper) GetQrFromStore(w http.ResponseWriter, r *http return } + // ------------- Optional query parameter "issuer" ------------- + + err = runtime.BindQueryParameter("form", true, false, "issuer", r.URL.Query(), ¶ms.Issuer) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "issuer", Err: err}) + return + } + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.GetQrFromStore(w, r, params) })) diff --git a/internal/api/authentication_test.go b/internal/api/authentication_test.go index df43feec4..e2d599f49 100644 --- a/internal/api/authentication_test.go +++ b/internal/api/authentication_test.go @@ -308,3 +308,18 @@ func checkQRfetchURL(t *testing.T, qrLink string) string { require.NoError(t, err) return fetchURL } + +func checkQRFetchURLForLinks(t *testing.T, qrLink string) string { + t.Helper() + qrURL, err := url.Parse(qrLink) + require.NoError(t, err) + assert.Equal(t, "iden3comm", qrURL.Scheme) + vals, err := url.ParseQuery(qrURL.RawQuery) + require.NoError(t, err) + val, found := vals["request_uri"] + require.True(t, found) + fetchURL, err := url.QueryUnescape(val[0]) + require.NoError(t, err) + fetchURL = fetchURL + "&issuer=" + vals["issuer"][0] + return fetchURL +} diff --git a/internal/api/links.go b/internal/api/links.go index 5f983d789..7f33d1030 100644 --- a/internal/api/links.go +++ b/internal/api/links.go @@ -92,9 +92,18 @@ func (s *Server) CreateLinkQrCodeCallback(ctx context.Context, request CreateLin return CreateLinkQrCodeCallback400JSONResponse{N400JSONResponse{"Cannot proceed with empty body"}}, nil } - offer, err := s.linkService.ProcessCallBack(ctx, *request.Body, request.Params.SessionID, request.Params.LinkID, s.cfg.ServerUrl) + issuerDID, err := w3c.ParseDID(request.Identifier) + if err != nil { + log.Error(ctx, "parsing issuer did", "err", err, "did", request.Identifier) + return CreateLinkQrCodeCallback400JSONResponse{N400JSONResponse{Message: "invalid issuer did"}}, nil + } + + offer, err := s.linkService.ProcessCallBack(ctx, *issuerDID, *request.Body, request.Params.LinkID, s.cfg.ServerUrl) if err != nil { log.Error(ctx, "error issuing the claim", "error", err) + if errors.Is(err, services.ErrLinkAlreadyExpired) || errors.Is(err, services.ErrLinkMaxExceeded) || errors.Is(err, services.ErrLinkInactive) { + return CreateLinkQrCodeCallback400JSONResponse{N400JSONResponse{Message: "error: " + err.Error()}}, nil + } return CreateLinkQrCodeCallback500JSONResponse{ N500JSONResponse{ Message: "error processing the callback", @@ -192,13 +201,6 @@ func (s *Server) CreateLinkQrCode(ctx context.Context, req CreateLinkQrCodeReque return CreateLinkQrCode500JSONResponse{N500JSONResponse{"Unexpected error while creating qr code"}}, nil } - // Backward compatibility. If the type is raw, we return the raw qr code - qrCodeRaw, err := s.qrService.Find(ctx, createLinkQrCodeResponse.QrID) - if err != nil { - log.Error(ctx, "qr store. Finding qr", "err", err, "id", createLinkQrCodeResponse.QrID) - return CreateLinkQrCode500JSONResponse{N500JSONResponse{"error looking for qr body"}}, nil - } - return CreateLinkQrCode200JSONResponse{ Issuer: IssuerDescription{ DisplayName: s.cfg.IssuerName, @@ -206,8 +208,7 @@ func (s *Server) CreateLinkQrCode(ctx context.Context, req CreateLinkQrCodeReque }, DeepLink: createLinkQrCodeResponse.DeepLink, UniversalLink: createLinkQrCodeResponse.UniversalLink, - QrCodeRaw: string(qrCodeRaw), - SessionID: createLinkQrCodeResponse.SessionID, + QrCodeRaw: createLinkQrCodeResponse.QrCodeRaw, LinkDetail: getLinkSimpleResponse(*createLinkQrCodeResponse.Link), }, nil } diff --git a/internal/api/links_test.go b/internal/api/links_test.go index f39a2b291..f2050b42e 100644 --- a/internal/api/links_test.go +++ b/internal/api/links_test.go @@ -1001,7 +1001,7 @@ func TestServer_CreateLinkQRCode(t *testing.T) { realQR := protocol.AuthorizationRequestMessage{} - qrLink := checkQRfetchURL(t, response.DeepLink) + qrLink := checkQRFetchURLForLinks(t, response.DeepLink) // Let's see that universal link is correct assert.Equal(t, server.cfg.UniversalLinks.BaseUrl+"#request_uri="+qrLink, response.UniversalLink) @@ -1017,17 +1017,13 @@ func TestServer_CreateLinkQRCode(t *testing.T) { assert.NotNil(t, realQR.Body) assert.Equal(t, "authentication", realQR.Body.Reason) - callbackArr := strings.Split(realQR.Body.CallbackURL, "sessionID") + callbackArr := strings.Split(realQR.Body.CallbackURL, "linkID") assert.True(t, len(callbackArr) == 2) assert.Equal(t, callBack, callbackArr[0]) - params := strings.Split(callbackArr[1], "linkID") - assert.True(t, len(params) == 2) - assert.NotNil(t, realQR.ID) assert.Equal(t, "https://iden3-communication.io/authorization/1.0/request", string(realQR.Type)) assert.Equal(t, "application/iden3comm-plain-json", string(realQR.Typ)) assert.Equal(t, did.String(), realQR.From) assert.NotNil(t, realQR.ThreadID) - assert.NotNil(t, response.SessionID) assert.Equal(t, tc.expected.linkDetail.Id, response.LinkDetail.Id) assert.Equal(t, tc.expected.linkDetail.SchemaType, response.LinkDetail.SchemaType) diff --git a/internal/api/qrcode.go b/internal/api/qrcode.go index ef7f8931a..3f9061242 100644 --- a/internal/api/qrcode.go +++ b/internal/api/qrcode.go @@ -3,6 +3,8 @@ package api import ( "context" + "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/polygonid/sh-id-platform/internal/log" ) @@ -15,7 +17,25 @@ func (s *Server) GetQrFromStore(ctx context.Context, request GetQrFromStoreReque body, err := s.qrService.Find(ctx, *request.Params.Id) if err != nil { log.Error(ctx, "qr store. Finding qr", "err", err, "id", *request.Params.Id) - return GetQrFromStore500JSONResponse{N500JSONResponse{"error looking for qr body"}}, nil + + if request.Params.Issuer == nil { + return GetQrFromStore400JSONResponse{N400JSONResponse{"error looking for qr body"}}, nil + } + + issuerDID, err := w3c.ParseDID(*request.Params.Issuer) + if err != nil { + log.Error(ctx, "parsing issuer did", "err", err, "did", *request.Params.Issuer) + return GetQrFromStore400JSONResponse{N400JSONResponse{"invalid issuer did"}}, nil + } + + link, err := s.linkService.GetByID(ctx, *issuerDID, *request.Params.Id) + + if link.AuthorizationRequestMessage == nil { + log.Error(ctx, "qr store. Finding qr", "err", err, "id", *request.Params.Id) + return GetQrFromStore400JSONResponse{N400JSONResponse{"error looking for qr body"}}, nil + } + + return NewQrContentResponse(link.AuthorizationRequestMessage.Bytes), nil } return NewQrContentResponse(body), nil } diff --git a/internal/core/domain/link.go b/internal/core/domain/link.go index 2715a2546..8752754a1 100644 --- a/internal/core/domain/link.go +++ b/internal/core/domain/link.go @@ -10,6 +10,7 @@ import ( "github.com/iden3/go-schema-processor/v2/verifiable" "github.com/iden3/iden3comm/v2" "github.com/iden3/iden3comm/v2/protocol" + "github.com/jackc/pgtype" "github.com/polygonid/sh-id-platform/internal/common" ) @@ -62,21 +63,22 @@ type LinkCoreDID w3c.DID // Link - represents a credential offer type Link struct { - ID uuid.UUID - IssuerDID LinkCoreDID - CreatedAt time.Time - MaxIssuance *int - ValidUntil *time.Time - SchemaID uuid.UUID - CredentialExpiration *time.Time - CredentialSignatureProof bool - CredentialMTPProof bool - CredentialSubject CredentialSubject - Active bool - Schema *Schema - IssuedClaims int // TODO: Give a value when link redemption is implemented - RefreshService *verifiable.RefreshService - DisplayMethod *verifiable.DisplayMethod + ID uuid.UUID + IssuerDID LinkCoreDID + CreatedAt time.Time + MaxIssuance *int + ValidUntil *time.Time + SchemaID uuid.UUID + CredentialExpiration *time.Time + CredentialSignatureProof bool + CredentialMTPProof bool + CredentialSubject CredentialSubject + Active bool + Schema *Schema + IssuedClaims int // TODO: Give a value when link redemption is implemented + RefreshService *verifiable.RefreshService + DisplayMethod *verifiable.DisplayMethod + AuthorizationRequestMessage *pgtype.JSONB `json:"authorization_request_message"` } // NewLink - Constructor diff --git a/internal/core/ports/identity_service.go b/internal/core/ports/identity_service.go index ef55e6caa..b3372baa2 100644 --- a/internal/core/ports/identity_service.go +++ b/internal/core/ports/identity_service.go @@ -54,6 +54,7 @@ type IdentityService interface { GetStates(ctx context.Context, issuerDID w3c.DID, filter *GetStateTransactionsRequest) ([]domain.IdentityState, uint, error) CreateAuthenticationQRCode(ctx context.Context, serverURL string, issuerDID w3c.DID) (*CreateAuthenticationQRCodeResponse, error) Authenticate(ctx context.Context, message string, sessionID uuid.UUID, serverURL string) (*protocol.AuthorizationResponseMessage, error) + AuthenticateWithRequest(ctx context.Context, sessionID *uuid.UUID, authReq protocol.AuthorizationRequestMessage, message string, serverURL string) (*protocol.AuthorizationResponseMessage, error) GetFailedState(ctx context.Context, identifier w3c.DID) (*domain.IdentityState, error) PublishGenesisStateToRHS(ctx context.Context, did *w3c.DID) error UpdateIdentityDisplayName(ctx context.Context, did w3c.DID, displayName string) error diff --git a/internal/core/ports/link_repository.go b/internal/core/ports/link_repository.go index 1cff5ff2b..bd1670594 100644 --- a/internal/core/ports/link_repository.go +++ b/internal/core/ports/link_repository.go @@ -5,6 +5,7 @@ import ( "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/iden3comm/v2/protocol" "github.com/polygonid/sh-id-platform/internal/core/domain" "github.com/polygonid/sh-id-platform/internal/db" @@ -16,4 +17,5 @@ type LinkRepository interface { GetByID(ctx context.Context, issuerID w3c.DID, id uuid.UUID) (*domain.Link, error) GetAll(ctx context.Context, issuerDID w3c.DID, status LinkStatus, query *string) ([]domain.Link, error) Delete(ctx context.Context, id uuid.UUID, issuerDID w3c.DID) error + AddAuthorizationRequest(ctx context.Context, linkID uuid.UUID, issuerDID w3c.DID, authorizationRequest *protocol.AuthorizationRequestMessage) error } diff --git a/internal/core/ports/link_service.go b/internal/core/ports/link_service.go index 2e097077d..a21553e1f 100644 --- a/internal/core/ports/link_service.go +++ b/internal/core/ports/link_service.go @@ -22,19 +22,19 @@ type CreateQRCodeResponse struct { DeepLink string UniversalLink string QrID uuid.UUID - SessionID string + QrCodeRaw string } // LinkStatus is a Link type request. All|Active|Inactive|Exceeded type LinkStatus string const ( - LinkAll LinkStatus = "all" // LinkAll : All links - LinkActive LinkStatus = "active" // LinkActive : Active links - LinkInactive LinkStatus = "inactive" // LinkInactive : Inactive links - LinkExceeded LinkStatus = "exceeded" // LinkExceeded : Expired links or with more credentials issued than expected - AgentUrl = "%s/v2/agent" // AgentUrl : Agent URL - LinksCallbackURL = "%s/v2/identities/%s/credentials/links/callback?sessionID=%s&linkID=%s" // LinksCallbackURL : Links callback URL + LinkAll LinkStatus = "all" // LinkAll : All links + LinkActive LinkStatus = "active" // LinkActive : Active links + LinkInactive LinkStatus = "inactive" // LinkInactive : Inactive links + LinkExceeded LinkStatus = "exceeded" // LinkExceeded : Expired links or with more credentials issued than expected + AgentUrl = "%s/v2/agent" // AgentUrl : Agent URL + LinksCallbackURL = "%s/v2/identities/%s/credentials/links/callback?linkID=%s" // LinksCallbackURL : Links callback URL ) // LinkTypeReqFromString constructs a LinkStatus from a string @@ -61,6 +61,5 @@ type LinkService interface { GetAll(ctx context.Context, issuerDID w3c.DID, status LinkStatus, query *string) ([]domain.Link, error) CreateQRCode(ctx context.Context, issuerDID w3c.DID, linkID uuid.UUID, serverURL string) (*CreateQRCodeResponse, error) IssueOrFetchClaim(ctx context.Context, issuerDID w3c.DID, userDID w3c.DID, linkID uuid.UUID, hostURL string) (*protocol.CredentialsOfferMessage, error) - ProcessCallBack(ctx context.Context, message string, sessionID uuid.UUID, linkID uuid.UUID, hostURL string) (*protocol.CredentialsOfferMessage, error) - GetQRCode(ctx context.Context, sessionID uuid.UUID, issuerID w3c.DID, linkID uuid.UUID) (*GetQRCodeResponse, error) + ProcessCallBack(ctx context.Context, issuerDID w3c.DID, message string, linkID uuid.UUID, hostURL string) (*protocol.CredentialsOfferMessage, error) } diff --git a/internal/core/ports/qrstore_service.go b/internal/core/ports/qrstore_service.go index 4e9cf49e4..cdf7c47fd 100644 --- a/internal/core/ports/qrstore_service.go +++ b/internal/core/ports/qrstore_service.go @@ -5,13 +5,21 @@ import ( "time" "github.com/google/uuid" + "github.com/iden3/go-iden3-core/v2/w3c" ) const ( // QRStoreUrl is the URL to the QR store service QRStoreUrl = "iden3comm://?request_uri=%s/v2/qr-store?id=%s" + + // QRStoreUrlWithDID is the URL to the QR store service with the issuer DID + QRStoreUrlWithDID = "iden3comm://?request_uri=%s/v2/qr-store?id=%s&issuer=%s" + // UniversalLinkURL - is the URL to the Universal Link UniversalLinkURL = "%s#request_uri=%s/v2/qr-store?id=%s" + + // UniversalLinkURLWithDID - is the URL to the Universal Link with the issuer DID + UniversalLinkURLWithDID = "%s#request_uri=%s/v2/qr-store?id=%s&issuer=%s" ) // QrStoreService is the interface that provides methods to store and retrieve the body of QR codes and to provide support @@ -19,6 +27,6 @@ const ( type QrStoreService interface { Find(ctx context.Context, id uuid.UUID) ([]byte, error) Store(ctx context.Context, qrCode []byte, ttl time.Duration) (uuid.UUID, error) - ToDeepLink(hostURL string, id uuid.UUID) string - ToUniversalLink(ULinkBaseUrl string, hostURL string, id uuid.UUID) string + ToDeepLink(hostURL string, id uuid.UUID, issuerDID *w3c.DID) string + ToUniversalLink(ULinkBaseUrl string, hostURL string, id uuid.UUID, issuerDID *w3c.DID) string } diff --git a/internal/core/ports/session_repository.go b/internal/core/ports/session_repository.go index f3ef3dad5..21f9f3822 100644 --- a/internal/core/ports/session_repository.go +++ b/internal/core/ports/session_repository.go @@ -4,14 +4,10 @@ import ( "context" "github.com/iden3/iden3comm/v2/protocol" - - link_state "github.com/polygonid/sh-id-platform/pkg/link" ) // SessionRepository defines the interface for managing sessions type SessionRepository interface { Get(ctx context.Context, key string) (protocol.AuthorizationRequestMessage, error) Set(ctx context.Context, key string, value protocol.AuthorizationRequestMessage) error - SetLink(ctx context.Context, key string, value link_state.State) error - GetLink(ctx context.Context, key string) (link_state.State, error) } diff --git a/internal/core/services/claims.go b/internal/core/services/claims.go index f040185e1..80e3b4860 100644 --- a/internal/core/services/claims.go +++ b/internal/core/services/claims.go @@ -373,8 +373,8 @@ func (c *claim) GetCredentialQrCode(ctx context.Context, issID *w3c.DID, id uuid return nil, err } return &ports.GetCredentialQrCodeResponse{ - DeepLink: c.qrService.ToDeepLink(hostURL, qrID), - UniversalLink: c.qrService.ToUniversalLink(c.cfg.BaseUrl, hostURL, qrID), + DeepLink: c.qrService.ToDeepLink(hostURL, qrID, nil), + UniversalLink: c.qrService.ToUniversalLink(c.cfg.BaseUrl, hostURL, qrID, nil), QrRaw: string(raw), SchemaType: getCredentialType(*claim), QrID: qrID, diff --git a/internal/core/services/identity.go b/internal/core/services/identity.go index 930773610..073185189 100644 --- a/internal/core/services/identity.go +++ b/internal/core/services/identity.go @@ -472,13 +472,7 @@ func (i *identity) UpdateIdentityState(ctx context.Context, state *domain.Identi return err } -func (i *identity) Authenticate(ctx context.Context, message string, sessionID uuid.UUID, serverURL string) (*protocol.AuthorizationResponseMessage, error) { - authReq, err := i.sessionManager.Get(ctx, sessionID.String()) - if err != nil { - log.Warn(ctx, "authentication session not found") - return nil, err - } - +func (i *identity) AuthenticateWithRequest(ctx context.Context, sessionID *uuid.UUID, authReq protocol.AuthorizationRequestMessage, message string, serverURL string) (*protocol.AuthorizationResponseMessage, error) { arm, err := i.verifier.FullVerify(ctx, message, authReq, pubsignals.WithAcceptedStateTransitionDelay(transitionDelay)) if err != nil { log.Error(ctx, "authentication failed", "err", err) @@ -522,7 +516,10 @@ func (i *identity) Authenticate(ctx context.Context, message string, sessionID u return err } - return i.connectionsRepository.SaveUserAuthentication(ctx, i.storage.Pgx, connID, sessionID, conn.CreatedAt) + if sessionID == nil { + sessionID = common.ToPointer(uuid.New()) + } + return i.connectionsRepository.SaveUserAuthentication(ctx, i.storage.Pgx, connID, *sessionID, conn.CreatedAt) }); err != nil { return nil, err } @@ -533,10 +530,18 @@ func (i *identity) Authenticate(ctx context.Context, message string, sessionID u log.Error(ctx, "sending connection notification", "err", err.Error(), "connection", connID) } } - return arm, nil } +func (i *identity) Authenticate(ctx context.Context, message string, sessionID uuid.UUID, serverURL string) (*protocol.AuthorizationResponseMessage, error) { + authReq, err := i.sessionManager.Get(ctx, sessionID.String()) + if err != nil { + log.Warn(ctx, "authentication session not found") + return nil, err + } + return i.AuthenticateWithRequest(ctx, &sessionID, authReq, message, serverURL) +} + func (i *identity) CreateAuthenticationQRCode(ctx context.Context, serverURL string, issuerDID w3c.DID) (*ports.CreateAuthenticationQRCodeResponse, error) { sessionID := uuid.New() reqID := uuid.New().String() @@ -566,7 +571,7 @@ func (i *identity) CreateAuthenticationQRCode(ctx context.Context, serverURL str return nil, err } return &ports.CreateAuthenticationQRCodeResponse{ - QRCodeURL: i.qrService.ToDeepLink(serverURL, linkID), + QRCodeURL: i.qrService.ToDeepLink(serverURL, linkID, nil), SessionID: sessionID, QrID: linkID, }, nil diff --git a/internal/core/services/link.go b/internal/core/services/link.go index d8fedb1a4..e684e7b73 100644 --- a/internal/core/services/link.go +++ b/internal/core/services/link.go @@ -23,7 +23,6 @@ import ( "github.com/polygonid/sh-id-platform/internal/loader" "github.com/polygonid/sh-id-platform/internal/log" "github.com/polygonid/sh-id-platform/internal/repositories" - linkState "github.com/polygonid/sh-id-platform/pkg/link" "github.com/polygonid/sh-id-platform/pkg/network" "github.com/polygonid/sh-id-platform/pkg/notifications" "github.com/polygonid/sh-id-platform/pkg/pubsub" @@ -166,6 +165,7 @@ func (ls *Link) Delete(ctx context.Context, id uuid.UUID, did w3c.DID) error { func (ls *Link) CreateQRCode(ctx context.Context, issuerDID w3c.DID, linkID uuid.UUID, serverURL string) (*ports.CreateQRCodeResponse, error) { link, err := ls.GetByID(ctx, issuerDID, linkID) if err != nil { + log.Error(ctx, "cannot fetch the link", "err", err) return nil, err } @@ -174,42 +174,45 @@ func (ls *Link) CreateQRCode(ctx context.Context, issuerDID w3c.DID, linkID uuid return nil, err } - sessionID := uuid.New().String() - reqID := uuid.New().String() - qrCode := &protocol.AuthorizationRequestMessage{ - From: issuerDID.String(), - ID: reqID, - ThreadID: reqID, - Typ: packers.MediaTypePlainMessage, - Type: protocol.AuthorizationRequestMessageType, - Body: protocol.AuthorizationRequestMessageBody{ - CallbackURL: fmt.Sprintf(ports.LinksCallbackURL, serverURL, issuerDID.String(), sessionID, linkID.String()), - Reason: authReason, - Scope: make([]protocol.ZeroKnowledgeProofRequest, 0), - }, - } - - err = ls.sessionManager.Set(ctx, sessionID, *qrCode) - if err != nil { - return nil, err - } - - raw, err := json.Marshal(qrCode) - if err != nil { - return nil, err - } + var authorizationRequestMessage *protocol.AuthorizationRequestMessage + var raw []byte + if link.AuthorizationRequestMessage == nil { + reqID := uuid.New().String() + authorizationRequestMessage = &protocol.AuthorizationRequestMessage{ + From: issuerDID.String(), + ID: reqID, + ThreadID: reqID, + Typ: packers.MediaTypePlainMessage, + Type: protocol.AuthorizationRequestMessageType, + Body: protocol.AuthorizationRequestMessageBody{ + CallbackURL: fmt.Sprintf(ports.LinksCallbackURL, serverURL, issuerDID.String(), linkID.String()), + Reason: authReason, + Scope: make([]protocol.ZeroKnowledgeProofRequest, 0), + }, + } + if err := ls.linkRepository.AddAuthorizationRequest(ctx, linkID, issuerDID, authorizationRequestMessage); err != nil { + log.Error(ctx, "cannot add the authorization request", "err", err) + return nil, err + } + raw, err = json.Marshal(authorizationRequestMessage) + if err != nil { + log.Error(ctx, "cannot marshal the authorization", "err", err) + } + } else { + if err := json.Unmarshal(link.AuthorizationRequestMessage.Bytes, &authorizationRequestMessage); err != nil { + log.Error(ctx, "cannot unmarshal the authorization", "err", err) + return nil, err + } + raw = link.AuthorizationRequestMessage.Bytes - id, err := ls.qrService.Store(ctx, raw, DefaultQRBodyTTL) - if err != nil { - return nil, err } return &ports.CreateQRCodeResponse{ - SessionID: sessionID, - DeepLink: ls.qrService.ToDeepLink(serverURL, id), - UniversalLink: ls.qrService.ToUniversalLink(ls.cfg.BaseUrl, serverURL, id), - QrID: id, + DeepLink: ls.qrService.ToDeepLink(serverURL, linkID, &issuerDID), + UniversalLink: ls.qrService.ToUniversalLink(ls.cfg.BaseUrl, serverURL, link.ID, &issuerDID), + QrID: link.ID, Link: link, + QrCodeRaw: string(raw), }, nil } @@ -313,8 +316,20 @@ func (ls *Link) IssueOrFetchClaim(ctx context.Context, issuerDID w3c.DID, userDI } // ProcessCallBack - process the callback. -func (ls *Link) ProcessCallBack(ctx context.Context, message string, sessionID uuid.UUID, linkID uuid.UUID, hostURL string) (*protocol.CredentialsOfferMessage, error) { - arm, err := ls.identityService.Authenticate(ctx, message, sessionID, hostURL) +func (ls *Link) ProcessCallBack(ctx context.Context, issuerID w3c.DID, message string, linkID uuid.UUID, hostURL string) (*protocol.CredentialsOfferMessage, error) { + link, err := ls.linkRepository.GetByID(ctx, issuerID, linkID) + if err != nil { + log.Error(ctx, "error fetching the link from the database", "err", err) + return nil, err + } + + var authenticationRequest protocol.AuthorizationRequestMessage + if err := json.Unmarshal(link.AuthorizationRequestMessage.Bytes, &authenticationRequest); err != nil { + log.Error(ctx, "error unmarshaling the authorization request", "err", err) + return nil, err + } + + arm, err := ls.identityService.AuthenticateWithRequest(ctx, nil, authenticationRequest, message, hostURL) if err != nil { log.Error(ctx, "error authenticating", "err", err.Error()) return nil, err @@ -340,25 +355,6 @@ func (ls *Link) ProcessCallBack(ctx context.Context, message string, sessionID u return offer, nil } -// GetQRCode - return the link qr code. -func (ls *Link) GetQRCode(ctx context.Context, sessionID uuid.UUID, issuerID w3c.DID, linkID uuid.UUID) (*ports.GetQRCodeResponse, error) { - link, err := ls.GetByID(ctx, issuerID, linkID) - if err != nil { - log.Error(ctx, "error fetching the link from the database", "err", err) - return nil, err - } - - linkStateInCache, err := ls.sessionManager.GetLink(ctx, linkState.CredentialStateCacheKey(linkID.String(), sessionID.String())) - if err != nil { - log.Error(ctx, "error fetching the link state from the cache", "err", err) - return nil, err - } - return &ports.GetQRCodeResponse{ - State: &linkStateInCache, - Link: link, - }, nil -} - func (ls *Link) validate(ctx context.Context, link *domain.Link) error { if link.ValidUntil != nil && time.Now().UTC().After(*link.ValidUntil) { log.Debug(ctx, "cannot issue a credential for an expired link") diff --git a/internal/core/services/qrstore.go b/internal/core/services/qrstore.go index 0edadfa77..36bf3a741 100644 --- a/internal/core/services/qrstore.go +++ b/internal/core/services/qrstore.go @@ -8,6 +8,7 @@ import ( "time" "github.com/google/uuid" + "github.com/iden3/go-iden3-core/v2/w3c" "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/internal/log" @@ -60,12 +61,19 @@ func (s *QrStoreService) Store(ctx context.Context, qrCode []byte, ttl time.Dura } // ToDeepLink constructs a deeplink that will be used to get the body of a QR code. -func (s *QrStoreService) ToDeepLink(hostURL string, id uuid.UUID) string { +func (s *QrStoreService) ToDeepLink(hostURL string, id uuid.UUID, issuerDID *w3c.DID) string { + if issuerDID != nil { + return fmt.Sprintf(ports.QRStoreUrlWithDID, hostURL, id.String(), issuerDID.String()) + } + return fmt.Sprintf(ports.QRStoreUrl, hostURL, id.String()) } // ToUniversalLink constructs a universal link -func (s *QrStoreService) ToUniversalLink(uLinkBaseUrl string, hostURL string, id uuid.UUID) string { +func (s *QrStoreService) ToUniversalLink(uLinkBaseUrl string, hostURL string, id uuid.UUID, issuerDID *w3c.DID) string { + if issuerDID != nil { + return fmt.Sprintf(ports.UniversalLinkURLWithDID, uLinkBaseUrl, hostURL, id.String(), issuerDID.String()) + } return fmt.Sprintf(ports.UniversalLinkURL, uLinkBaseUrl, hostURL, id.String()) } diff --git a/internal/db/schema/migrations/202409120950120_add_authorizartion_req_msg_to_links_table.sql b/internal/db/schema/migrations/202409120950120_add_authorizartion_req_msg_to_links_table.sql new file mode 100644 index 000000000..afe5a1339 --- /dev/null +++ b/internal/db/schema/migrations/202409120950120_add_authorizartion_req_msg_to_links_table.sql @@ -0,0 +1,12 @@ + +-- +goose Up +-- +goose StatementBegin +ALTER TABLE links + ADD COLUMN authorization_request_message jsonb NULL; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +ALTER TABLE links +DROP COLUMN authorization_request_message; +-- +goose StatementEnd diff --git a/internal/repositories/link.go b/internal/repositories/link.go index a4562cd66..0e3741ce5 100644 --- a/internal/repositories/link.go +++ b/internal/repositories/link.go @@ -11,6 +11,7 @@ import ( "github.com/google/uuid" "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/iden3comm/v2/protocol" "github.com/jackc/pgtype" "github.com/jackc/pgx/v4" @@ -76,6 +77,7 @@ SELECT links.id, links.refresh_service, links.display_method, count(claims.id) as issued_claims, + links.authorization_request_message, schemas.id as schema_id, schemas.issuer_id as schema_issuer_id, schemas.url, @@ -107,6 +109,7 @@ GROUP BY links.id, schemas.id &link.RefreshService, &link.DisplayMethod, &link.IssuedClaims, + &link.AuthorizationRequestMessage, &s.ID, &s.IssuerID, &s.URL, @@ -265,3 +268,9 @@ func (l link) Delete(ctx context.Context, id uuid.UUID, issuerDID w3c.DID) error } return nil } + +func (l link) AddAuthorizationRequest(ctx context.Context, linkID uuid.UUID, issuerDID w3c.DID, authorizationRequest *protocol.AuthorizationRequestMessage) error { + const sql = `UPDATE links SET authorization_request_message = $1 WHERE id = $2 AND issuer_id = $3` + _, err := l.conn.Pgx.Exec(ctx, sql, authorizationRequest, linkID, issuerDID.String()) + return err +} diff --git a/internal/repositories/session.go b/internal/repositories/session.go index 734b2e27f..1e23ca0f7 100644 --- a/internal/repositories/session.go +++ b/internal/repositories/session.go @@ -9,7 +9,6 @@ import ( "github.com/polygonid/sh-id-platform/internal/core/ports" "github.com/polygonid/sh-id-platform/pkg/cache" - link_state "github.com/polygonid/sh-id-platform/pkg/link" ) const ( @@ -40,17 +39,3 @@ func (c *cached) Get(ctx context.Context, key string) (protocol.AuthorizationReq func (c *cached) Set(ctx context.Context, key string, value protocol.AuthorizationRequestMessage) error { return c.cache.Set(ctx, key, value, defaultTTL) } - -// SetLink - stores the given session information -func (c *cached) SetLink(ctx context.Context, key string, value link_state.State) error { - return c.cache.Set(ctx, key, value, defaultTTL) -} - -func (c *cached) GetLink(ctx context.Context, key string) (link_state.State, error) { - var message link_state.State - found := c.cache.Get(ctx, key, &message) - if !found { - return message, fmt.Errorf("link state not found") - } - return message, nil -}