Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correction misinterpretation of Presentation Exchange for OpenID #60

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
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
43 changes: 27 additions & 16 deletions Docs/InternalArchitecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,42 @@
## Creating a Verified Id Request from Input
```mermaid
classDiagram
RequestHandler ..> VerifiedIdRequestInput: uses
RequestResolver ..> AdditionalRequestParams: uses
RequestResolver ..|> RawRequest: creates
RequestResolver ..|> VerifiedIdRequestInput: uses
RequestHandler ..> RequestResolver: uses
RequestProcessor ..|> VerifiedIdRequest: creates
AdditionalRequestParams <.. RequestProcessor: uses
RawRequest <.. RequestProcessor: uses
class RequestHandlerFactory {
- requestHandlers: [RequestHandler]
~getHandler(from resolver: RequestResolver) RequestHandler
}
RequestHandlerFactory o-- RequestHandler
RequestHandlerFactory ..> RequestResolver: uses
RequestHandlerFactory ..> RequestHandler: creates

RawRequest ..> RequestHandler: uses
RequestResolver ..> RawRequest: creates
RequestResolver ..> VerifiedIdRequestInput: uses
RequestProcessor ..> VerifiedIdRequest: creates
RequestHandler ..> VerifiedIdRequest: creates
RawRequest ..> RequestProcessor: uses
<<Interface>> RequestResolver
class RequestResolver {
~canResolve(using: RequestHandler) Bool
~canResolve(from: VerifiedIdClientInput) Bool
~resolve(input: VerifiedIdClientInput, using: [AdditionalRequestParams]) RawRequest
~resolve(input: VerifiedIdClientInput) RawRequest
}
class RequestHandler{
~handle(input: VerifiedIdRequestInput, using: RequestResolver) VerifiedIdRequest
~handle(input: RawRequest) VerifiedIdRequest
}
<<Interface>> VerifiedIdRequestInput
class RequestProcessorFactory {
- requestProcessors: [RequestProcessor]
~getRequestProcessor(for: RawRequest) RequestProcessor
}
<<Interface>> RequestProcessor
class RequestProcessor {
-requestParams: AdditionalRequestParams
~canProcess(raw: RawRequest) Bool
~process(raw: RawRequest) VerifiedIdRequest
}
<<Interface>> AdditionalRequestParams
RequestProcessorFactory o-- RequestProcessor
RequestHandler ..> RequestProcessor: uses
RequestHandler *-- RequestProcessorFactory
<<Interface>> RawRequest
<<Interface>> VerifiedIdRequest
class VerifiedIdRequest {
Expand All @@ -44,14 +55,14 @@ A Request Resolver resolves a raw request from a request input and additional pa
Ex: An `OpenIdURLRequestResolver` would know how to resolve a raw open id request token. The additional params would be OpenId specific to be used to send any additional information needed to resolve the request (what version of openid is supported, for example). The result would be a `OpenIdRawRequest` that has not been processed or validated yet.

### Request Handler
A request handler is used to handle an input where the request can be resolved by a request resolver and then processed, validated and mapped to a Verified Id Request. A request handler is protocol specific (e.g. `OpenIdRequestHandler`). It does not need to know to resolve the input as that logic is handles by the resolver, but it can inject any additional parameters into the resolver using Additional Params.
A request handler is used to handle an input where the request can be resolved by a request resolver and then processed, validated and mapped to a Verified Id Request. A request handler is protocol specific (e.g. `OpenIdRequestHandler`). It does not need to know to resolve the input as that logic is handles by the resolver.

Ex: An `OpenIdRequestHandler` would take in any type of input as long as the resolver that is also passed in knows how to resolve the input into an OpenIdRawRequest. The `OpenIdRequestHandler` would contain a list of `OpenIdRequestProcessors` that know how to process different types of openid version (e.g. jwt 0.1, jwt 0.2, json-ld, etc). The handler would use the `OpenIdAdditionalRequestParams` that are passed into the resolver to tell the resolver what version of open-id are supported.
Ex: An `OpenIdRequestHandler` would take in any type of input as long as the resolver that is also passed in knows how to resolve the input into an OpenIdRawRequest. The `OpenIdRequestHandler` would contain a list of `OpenIdRequestProcessors` that know how to process different types of openid version or extensions (e.g. jwt 0.1, jwt 0.2, json-ld, etc). `OpenIdRequestHandler` can parse the base OpenID request, then use `OpenIDRequestProcessors` for verified ID logic to form responses, then serialize and send the response according to openID protocol.

### Request Processor
A request processor is used to process a Raw Request and return a Verified Id Request. A request processor is protocol-version specific.
A request processor is used to process a Raw Request and return a Verified Id Request. A request processor is protocol-version specific logic.

Ex. A `JWTV1RequestProcessor` takes in a `OpenIdRawRequest` and processes, validates, and maps it to a Verified Id Request.
Ex. A `JWTV1RequestProcessor` takes in a `OpenIdRawRequest` and processes, validates, and maps it to a Verified Id Request.

## Configuring the Request Handler
```mermaid
Expand Down
114 changes: 114 additions & 0 deletions Docs/PresentationExchange.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# OpenID Presentation Exchange Request Processor
[Presentation Exchange](https://identity.foundation/presentation-exchange/) is a data format defining the exchange of credentials. Combined with the [OpenID Connect Verifiable Presentations](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) extension creates a complete verified id request and response flow.

```mermaid
classDiagram

class OpenIdRawRequest {
- type: RequestType
- raw: Data?
- promptValueForIssuance: String
}

RawRequest --|> OpenIdRawRequest

RawRequest ..> RequestHandling: uses
RawRequest ..> RequestProcessor: uses

<<interface>> RequestHandling
class RequestHandling {
~handle(input: RawRequest) VerifiedIdRequest
}

class OpenIdRequestHandler {
- configuration: LibraryConfiguration
- openIdResponder: OpenIdResponder
- manifestResolver: ManifestResolver
- verifiedIdRequester: VerifiedIdRequester
~handleRequest(request: RawRequest): VerifiedIdRequest
}

class OpenIdPresentationRequest {
- rawRequest: OpenIdRawRequest
- responder: OpenIdResponder
- configuration: LibraryConfiguration
}
OpenIdPresentationRequest ..> PresentationResponseContainer: uses

class VerifiedIdRequirement {
- encrypted: Bool
- required: Bool
- types: [String]
- purpose: String?
- issuanceOptions: [VerifiedIdRequestInput]
- id: String?
- constraint: VerifiedIdConstraint
- selectedVerifiedId: VerifiedId?
- exclusivePlacement: [String]
~getMatches(verifeidIds: [VerifiedId]): [VerifiedId]
~fulfill(verifiedId: VerifiedId): VerifiedIdResult<null>

~exclusive(ids: [String])
}

class PresentationResponseContainer {
- request: PresentationRequest
- expiryInSeconds: Int
- audienceUrl: String
- audienceDid: String
- nonce: String
- requestedIdTokenMap: [:]
- requestedSelfAttestedClaimMap: [:]
- requestedVCMap: [:]
~addVerifiableCredential(id: String, vc: VerifiableCredential)
~add(requirement: Requirement)

}
PresentationResponseContainer ..> VerifiedIdRequirement: uses

OpenIdPresentationRequest o-- VerifiedIdRequirement

VerifiedIdRequest --|> OpenIdPresentationRequest

RequestHandling --|> OpenIdRequestHandler
OpenIdRawRequest ..> OpenIdRequestHandler: uses

class RequestProcessorFactory {
- requestProcessors: [RequestProcessor]
~getRequestProcessor(for: RawRequest) RequestProcessor
}
<<Interface>> RequestProcessor
class RequestProcessor {
~canProcess(raw: RawRequest) Bool
~process(raw: RawRequest) VerifiedIdRequest
}

class OpenIdPresentationExchangeRequestProcessor {
~canProcess(raw: RawRequest) Bool
~process(raw: RawRequest) VerifiedIdRequest
}

RequestProcessor --|> OpenIdPresentationExchangeRequestProcessor

RequestProcessorFactory o-- OpenIdPresentationExchangeRequestProcessor
OpenIdPresentationExchangeRequestProcessor ..> OpenIdPresentationRequest: creates
OpenIdRequestHandler *-- RequestProcessorFactory
<<Interface>> RawRequest
<<Interface>> VerifiedIdRequest
class VerifiedIdRequest {
- style: RequesterStyle
- requirement: Requirement
- rootOfTrust: RootOfTrust
~isSatisfied(): Bool
~complete(): VerifiedIdResult<T>
~cancel(message: String?): VerifiedIdResult<null>
}
```

`OpenIdPresentationExchangeRequestProcessor` can be called by `OpenIdRequestHandler` with the request. If the request is an `OpenIdRawRequest`, the processor will parse the `raw` value for a definition and form the corresponding `VerifiedIdRequest`.

In parsing, it converts `input_definition`s into `VerifiedIdRequirement`s. `VerifiedIdRequirement` is fulfilled by supplying a `VerifiedId` that matches constraints.

Once all `VerifiedIdRequirement`s have been satisfied, the OpenIdPresentationRequest can `complete()`. This will create a `PresentationResponseContainer` and `add` each requirement to the container. Once added, the container can be serialized, signed, and send according to the request's parameters.

**Proposed Change**: `exclusivePlacement` can be added to `VerifiedIdRequirement` with `exclusive(with: [definition_id])` to ensure input definitions / requirements do not share the same verifiable presentation. `PresentationResponseContainer` will note these exclusions and any subject conflicts to create and map as many verifiable presentations as required.
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,22 @@
struct RequestedClaims: Codable, Equatable {

/// Request Verifiable Presentation Tokens.
let vpToken: [RequestedVPToken]
let vpToken: RequestedVPToken

enum CodingKeys: String, CodingKey {
case vpToken = "vp_token"
}

init(vpToken: [RequestedVPToken]) {
init(vpToken: RequestedVPToken) {
self.vpToken = vpToken
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

if let vpTokens = try? container.decode([RequestedVPToken].self, forKey: .vpToken) {
if let vpTokens = try? container.decode(RequestedVPToken.self, forKey: .vpToken) {
self.vpToken = vpTokens
} else if let vpToken = try? container.decode(RequestedVPToken.self, forKey: .vpToken) {
self.vpToken = [vpToken]
} else {
} else {
throw DecodingError.unableToDecodeToken
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,29 +64,25 @@ struct PresentationResponseContainer: ResponseContaining {

mutating func addVerifiableCredential(id: String, vc: VerifiableCredential) throws {

guard let vpTokenRequests = request?.content.claims?.vpToken,
!vpTokenRequests.isEmpty else {
guard let vpTokenRequest = request?.content.claims?.vpToken else {
throw PresentationResponseError.noVerifiablePresentationRequestsInRequest
}

for vpTokenRequest in vpTokenRequests {
guard let presentationDefinition = vpTokenRequest.presentationDefinition,
let presentationDefinitionId = presentationDefinition.id else {
throw PresentationResponseError.noPresentationDefinitionInVerifiablePresentationRequest
}

if let inputDescriptors = presentationDefinition.inputDescriptors,
inputDescriptors.contains(where: { $0.id == id }) {

let mapping = RequestedVerifiableCredentialMapping(id: id, verifiableCredential: vc)

guard let presentationDefinition = vpTokenRequest.presentationDefinition,
let presentationDefinitionId = presentationDefinition.id else {
throw PresentationResponseError.noPresentationDefinitionInVerifiablePresentationRequest
}
var mappings = requestVCMap[presentationDefinitionId] ?? []
mappings.append(mapping)

if let inputDescriptors = presentationDefinition.inputDescriptors,
inputDescriptors.contains(where: { $0.id == id }) {

let mapping = RequestedVerifiableCredentialMapping(id: id, verifiableCredential: vc)

var mappings = requestVCMap[presentationDefinitionId] ?? []
mappings.append(mapping)

requestVCMap.updateValue(mappings, forKey: presentationDefinitionId)
return
}
requestVCMap.updateValue(mappings, forKey: presentationDefinitionId)
return
}

throw PresentationResponseError.noInputDescriptorMatchesGivenId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@ extension RequestedClaims: Mappable
{
var requirements: [Requirement] = []

for vpTokenRequest in vpToken
if let presentationDefinition = vpToken.presentationDefinition
{
if let presentationDefinition = vpTokenRequest.presentationDefinition
{
requirements.append(contentsOf: try mapper.map(presentationDefinition))
}
requirements.append(contentsOf: try mapper.map(presentationDefinition))
}

if requirements.isEmpty
Expand Down
Loading