Skip to content

Commit

Permalink
chore: update link qr flow (#781)
Browse files Browse the repository at this point in the history
* chore: update link qr flow

* fix: test
  • Loading branch information
martinsaporiti authored Sep 13, 2024
1 parent 1e48675 commit 6293979
Show file tree
Hide file tree
Showing 19 changed files with 199 additions and 153 deletions.
9 changes: 5 additions & 4 deletions api/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1262,7 +1267,6 @@ paths:
- Links
parameters:
- $ref: '#/components/parameters/pathIdentifier'
- $ref: '#/components/parameters/sessionID'
- $ref: '#/components/parameters/linkID'
requestBody:
required: true
Expand Down Expand Up @@ -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'

Expand Down
30 changes: 10 additions & 20 deletions internal/api/api.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions internal/api/authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
21 changes: 11 additions & 10 deletions internal/api/links.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -192,22 +201,14 @@ 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,
Logo: s.cfg.IssuerLogo,
},
DeepLink: createLinkQrCodeResponse.DeepLink,
UniversalLink: createLinkQrCodeResponse.UniversalLink,
QrCodeRaw: string(qrCodeRaw),
SessionID: createLinkQrCodeResponse.SessionID,
QrCodeRaw: createLinkQrCodeResponse.QrCodeRaw,
LinkDetail: getLinkSimpleResponse(*createLinkQrCodeResponse.Link),
}, nil
}
Expand Down
8 changes: 2 additions & 6 deletions internal/api/links_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

Expand Down
22 changes: 21 additions & 1 deletion internal/api/qrcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package api
import (
"context"

"github.com/iden3/go-iden3-core/v2/w3c"

"github.com/polygonid/sh-id-platform/internal/log"
)

Expand All @@ -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
}
32 changes: 17 additions & 15 deletions internal/core/domain/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions internal/core/ports/identity_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions internal/core/ports/link_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
}
17 changes: 8 additions & 9 deletions internal/core/ports/link_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
12 changes: 10 additions & 2 deletions internal/core/ports/qrstore_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,28 @@ 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
// to the QR url shortener functionality.
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
}
4 changes: 0 additions & 4 deletions internal/core/ports/session_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
4 changes: 2 additions & 2 deletions internal/core/services/claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 6293979

Please sign in to comment.