diff --git a/visa-certificate/README.md b/visa-certificate/README.md new file mode 100644 index 0000000..cfcd531 --- /dev/null +++ b/visa-certificate/README.md @@ -0,0 +1,10 @@ +# Visa Certificate + +This folder contains all technical assets related to the **Visa Certificate**. +It includes credential schemas, field definitions, mappings, and translation files used for credential generation and validation. + +## Available Versions + +Choose a version of the credential type to view its specification. Currently, the latest version is: + +- [`v1`](v1/) – the first version of the Visa. diff --git a/visa-certificate/v1/README.md b/visa-certificate/v1/README.md new file mode 100644 index 0000000..7445dd6 --- /dev/null +++ b/visa-certificate/v1/README.md @@ -0,0 +1,11 @@ +# Visa Certificate (v1) + +This folder defines the structure and resources for the v1 implementation of **Visa Certificate**. + +## Contents + +- [edc](edc) - European Digital Credentials (EDC) schemas, mappings, example credentials and signed files. +- [input-fields](input-fields) - Input field schema definitions, configuration examples and related translation files. +- [translations](translations) - Translated credential titles organised by language. +- [user-consent](user-consent) - Mapping between user consent groups and corresponding input fields. + \ No newline at end of file diff --git a/visa-certificate/v1/edc/README.md b/visa-certificate/v1/edc/README.md new file mode 100644 index 0000000..56f7b63 --- /dev/null +++ b/visa-certificate/v1/edc/README.md @@ -0,0 +1,9 @@ +# EDC + +This folder contains resources related to the **European Digital Credentials (EDC)** format, a serialization of the ELM ontology, used for Visa Certificate. + +## Contents + +- [schema](schema.json) - JSON Schema definition of the EDC Visa Certificate. +- [input-fields-to-credential-map](input-fields-to-credential-map.json) - Mapping between user-friendly input fields and the EDC Visa Certificate fields. + \ No newline at end of file diff --git a/visa-certificate/v1/edc/input-fields-to-credential-map.json b/visa-certificate/v1/edc/input-fields-to-credential-map.json new file mode 100644 index 0000000..1015539 --- /dev/null +++ b/visa-certificate/v1/edc/input-fields-to-credential-map.json @@ -0,0 +1,13 @@ +{ + "fullName": "/credentialSubject/fullName", + "durationOfStay": "/credentialSubject/durationOfStay", + "issuanceDate":"/issuanceDate", + "validUntil": "/validUntil", + "visaNumber": "/credentialSubject/visaNumber", + "passportNumber": "/credentialSubject/passportNumber", + "placeOfIssue": "/credentialSubject/placeOfIssue", + "nationality": "/credentialSubject/citizenshipCountry/prefLabel", + "dateOfBirth" : "/credentialSubject/dateOfBirth", + "typeOfVisa": "/credentialSubject/typeOfVisa", + "entryType": "/credentialSubject/entryType" +} \ No newline at end of file diff --git a/visa-certificate/v1/edc/schema.json b/visa-certificate/v1/edc/schema.json new file mode 100644 index 0000000..ffa250f --- /dev/null +++ b/visa-certificate/v1/edc/schema.json @@ -0,0 +1,608 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Schema Template", + "type": "object", + "properties": { + "@context": { + "type": "array", + "items": { + "type": "string" + }, + "const": [ + "https://www.w3.org/2018/credentials/v1", + "http://data.europa.eu/snb/model/context/edc-ap" + ], + "minItems": 2, + "maxItems": 2, + "uniqueItems": true + }, + "id": { + "type": "string", + "format": "uri" + }, + "type": { + "type": "array", + "items": { + "type": "string" + }, + "const": [ + "VerifiableCredential", + "EuropeanDigitalCredential" + ], + "minItems": 2, + "maxItems": 2, + "uniqueItems": true + }, + "issuer": { + "$ref": "#/definitions/organisation" + }, + "issued": { + "type": "string", + "format": "date-time", + "description": "Date the certificate was issued." + }, + "issuanceDate": { + "type": "string", + "format": "date-time", + "description": "Date the certificate was issued." + }, + "validFrom": { + "type": "string", + "format": "date-time", + "description": "Date the certificate becomes valid." + }, + "validUntil": { + "type": "string", + "format": "date-time", + "description": "Date the certificate becomes expired." + }, + "expirationDate": { + "type": "string", + "format": "date-time" + }, + "credentialSubject": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri" + }, + "type": { + "type": "string", + "const": "Person" + }, + "fullName": { + "$ref": "#/definitions/languageMap", + "description": "Full name of the credential subject in various languages." + }, + "dateOfBirth": { + "type": "string", + "format": "date-time" + }, + "citizenshipCountry": { + "$ref": "#/definitions/concept" + }, + "passportNumber": { + "type": "string" + }, + "visaNumber": { + "type": "string", + "description": "Person's VISA number" + }, + "typeOfVisa": { + "$ref": "#/definitions/languageMap" + }, + "placeOfIssue": { + "$ref": "#/definitions/languageMap" + }, + "entryType": { + "$ref": "#/definitions/languageMap" + }, + "durationOfStay": { + "type": "string", + "format": "duration" + }, + "hasClaim": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri" + }, + "type": { + "type": "string" + }, + "title": { + "$ref": "#/definitions/languageMap", + "description": "Title of the claim in various languages." + }, + "description": { + "$ref": "#/definitions/languageMap", + "description": "Description of the claim in various languages." + }, + "awardedBy": { + "type": "object", + "properties": { + "id": { + "type": "string", + "const": "urn:epass:awardingProcess:1", + "format": "uri", + "description": "A unique URI identifier for the awarding process." + }, + "type": { + "type": "string", + "const": "AwardingProcess", + "description": "The type of the awarding process." + }, + "awardingBody": { + "$ref": "#/definitions/organisation" + } + }, + "required": [ + "id", + "type", + "awardingBody" + ] + } + }, + "required": [ + "id", + "type", + "awardedBy" + ] + }, + "contactPoint": { + "$ref": "#/definitions/contactPoint", + "description": "Contact point details of the group." + } + }, + "required": [ + "id", + "type", + "hasClaim", + "contactPoint" + ] + }, + "displayParameter": { + "$ref": "#/definitions/displayParameter", + "description": "Object that holds credential display properties" + }, + "credentialSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "format": "uri" + }, + "type": { + "type": "string", + "enum": [ + "ShaclValidator2017", + "JsonSchema" + ] + } + }, + "required": [ + "id", + "type" + ] + }, + "credentialProfiles": { + "$ref": "#/definitions/concept", + "description": "An object defining profiles applicable to the credential." + } + }, + "required": [ + "@context", + "id", + "type", + "credentialSchema", + "credentialSubject", + "credentialProfiles", + "issuer", + "validFrom" + ], + "additionalProperties": false, + "definitions": { + "displayParameter": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "A unique URI identifier for the display parameter." + }, + "type": { + "type": "string", + "enum": [ + "DisplayParameter" + ], + "description": "Defines the type of object." + }, + "title": { + "$ref": "#/definitions/languageMap", + "description": "Credential title to display on frontend" + }, + "individualDisplay": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "A unique URI identifier for the display parameter." + }, + "type": { + "type": "string", + "enum": [ + "IndividualDisplay" + ], + "description": "Defines the type of object." + }, + "displayDetail": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "A unique URI identifier for the display detail." + }, + "type": { + "type": "string", + "enum": [ + "DisplayDetail" + ], + "description": "Defines the type of object." + }, + "image": { + "$ref": "#/definitions/mediaObject", + "description": "Logo of the credential." + }, + "page": { + "type": "integer" + } + }, + "required": [ + "id", + "type", + "image" + ] + }, + "language": { + "$ref": "#/definitions/concept" + } + }, + "required": [ + "id", + "type", + "language", + "displayDetail" + ] + }, + "language": { + "$ref": "#/definitions/concept" + }, + "primaryLanguage": { + "$ref": "#/definitions/concept" + }, + "description": { + "$ref": "#/definitions/languageMap", + "description": "Description of the credential." + } + }, + "required": [ + "id", + "type", + "title", + "language" + ] + }, + "concept": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "A unique URI identifier for the concept." + }, + "inScheme": { + "$ref": "#/definitions/conceptScheme" + }, + "notation": { + "type": "string", + "description": "Notation for the concept." + }, + "prefLabel": { + "$ref": "#/definitions/languageMap" + }, + "type": { + "type": "string", + "const": "Concept", + "enum": [ + "Concept" + ] + } + }, + "required": [ + "id", + "type", + "inScheme", + "prefLabel" + ] + }, + "conceptScheme": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "A unique URI identifier for the concept scheme." + }, + "type": { + "type": "string", + "const": "ConceptScheme", + "enum": [ + "ConceptScheme" + ] + } + }, + "required": [ + "id", + "type" + ] + }, + "languageMap": { + "type": "object", + "patternProperties": { + "^[a-z]{2}$": { + "type": "string", + "description": "A string value for a specific language code." + } + }, + "additionalProperties": false, + "description": "A mapping of language codes to string values." + }, + "contactPoint": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri", + "const": "urn:epass:contactPoint:1", + "description": "A unique URI identifier for the contact point." + }, + "type": { + "type": "string", + "const": "ContactPoint", + "description": "The type of the contact point." + }, + "emailAddress": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri", + "const": "mailto:", + "description": "A unique URI identifier for the email address." + }, + "type": { + "type": "string", + "const": "Mailbox", + "description": "The type of the email address." + } + }, + "required": [ + "id", + "type" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "type", + "emailAddress" + ], + "additionalProperties": false + }, + "organisation": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri", + "description": "A unique URI identifier for the organisation." + }, + "type": { + "type": "string", + "const": "Organisation", + "description": "The type of the organisation." + }, + "location": { + "$ref": "#/definitions/location", + "description": "Location details of the organisation." + }, + "logo": { + "$ref": "#/definitions/mediaObject", + "description": "Logo of the organisation." + }, + "legalName": { + "$ref": "#/definitions/languageMap", + "description": "Legal name of the organisation in various languages." + }, + "registration": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri", + "description": "A unique URI identifier for the legal identifier." + }, + "type": { + "type": "string", + "const": "LegalIdentifier", + "description": "The type of the legal identifier." + }, + "notation": { + "type": "string", + "description": "Notation or code for the legal identifier." + }, + "spatial": { + "$ref": "#/definitions/concept", + "description": "Spatial information related to the legal identifier." + } + }, + "required": [ + "id", + "type", + "spatial" + ] + }, + "homepage": { + "$ref": "#/definitions/webResource" + } + }, + "required": [ + "id", + "type", + "location", + "legalName" + ], + "additionalProperties": false + }, + "location": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri", + "description": "A unique URI identifier for the location." + }, + "type": { + "type": "string", + "const": "Location", + "description": "The type of the location." + }, + "address": { + "$ref": "#/definitions/address", + "description": "Address details of the location." + }, + "description": { + "$ref": "#/definitions/languageMap", + "description": "Location description." + } + }, + "required": [ + "id", + "type", + "address" + ], + "additionalProperties": false + }, + "address": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri", + "description": "A unique URI identifier for the address." + }, + "type": { + "type": "string", + "const": "Address", + "description": "The type of the address." + }, + "countryCode": { + "$ref": "#/definitions/concept", + "description": "Country code for the address." + }, + "fullAddress": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri", + "description": "A unique URI identifier for the address." + }, + "type": { + "type": "string", + "const": "Note", + "description": "The type of the address." + }, + "noteLiteral": { + "$ref": "#/definitions/languageMap" + } + }, + "required": [ + "id", + "type", + "noteLiteral" + ] + } + }, + "required": [ + "id", + "type", + "countryCode" + ], + "additionalProperties": false + }, + "mediaObject": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri", + "description": "A unique URI identifier for the media object." + }, + "type": { + "type": "string", + "const": "MediaObject", + "description": "The type of the media object." + }, + "content": { + "type": "string", + "description": "The content of the media object (e.g., base64 encoded data)." + }, + "contentEncoding": { + "$ref": "#/definitions/concept", + "description": "Encoding type of the media content." + }, + "contentType": { + "$ref": "#/definitions/concept", + "description": "MIME type of the media content." + } + }, + "required": [ + "id", + "type", + "content", + "contentEncoding", + "contentType" + ], + "additionalProperties": false + }, + "webResource": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri", + "description": "A unique URI identifier for the web resource." + }, + "type": { + "type": "string", + "const": "WebResource", + "description": "The type of the web resource." + }, + "contentURL": { + "type": "string", + "format": "uri", + "description": "The URL of the web resource." + } + }, + "required": [ + "id", + "type", + "contentURL" + ], + "additionalProperties": false + } + } +} + \ No newline at end of file diff --git a/visa-certificate/v1/input-fields/README.md b/visa-certificate/v1/input-fields/README.md new file mode 100644 index 0000000..bf14822 --- /dev/null +++ b/visa-certificate/v1/input-fields/README.md @@ -0,0 +1,9 @@ +# Input fields + +Input fields are simple key-value pairs that let you provide verifiable credential inputs in a format-agnostic way. Define your data once and the credential engine handles the rest, constructing credentials in any format you need. + +Input fields are accompanied by + +- [translations](translations) - Translated input field labels organised by language. +- [example](example.json) - Input field examples. +- [schema](schema.json) - JSON Schema definition of input fields. diff --git a/visa-certificate/v1/input-fields/example.json b/visa-certificate/v1/input-fields/example.json new file mode 100644 index 0000000..71853fa --- /dev/null +++ b/visa-certificate/v1/input-fields/example.json @@ -0,0 +1,23 @@ +{ + "fullName": { + "en": "John Doe" + }, + "durationOfStay": "PT60H", + "issuanceDate":"2026-01-02T15:04:05Z", + "validUntil": "2030-01-02T15:04:05Z", + "visaNumber": "123456", + "passportNumber": "654321", + "placeOfIssue": { + "en": "Slovenia" + }, + "nationality": { + "en": "Slovenian" + }, + "dateOfBirth" : "1990-01-01T00:00:00Z", + "typeOfVisa": { + "en":"Tourist Visa" + }, + "entryType": { + "en":"Single Entry" + } +} \ No newline at end of file diff --git a/visa-certificate/v1/input-fields/schema.json b/visa-certificate/v1/input-fields/schema.json new file mode 100644 index 0000000..f70ad4f --- /dev/null +++ b/visa-certificate/v1/input-fields/schema.json @@ -0,0 +1,109 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Schema Template", + "type": "object", + "properties": { + "fullName": { + "title":"Full name", + "description": "The full name of the recipient of this credential", + "type": "object", + "x-format": "languageMap", + "patternProperties": { + "^[a-z]{2}$": { + "type": "string", + "description": "A string value for a specific language code." + } + }, + "additionalProperties": false + + }, + "durationOfStay":{ + "title":"Duration of stay", + "description": "Hours of stay.", + "type": "string", + "format": "duration" + }, + "issuanceDate":{ + "title": "Issuance date", + "description": "The date and time when the credential was issued, formatted in ISO 8601 (YYYY-MM-DDThh:mm:ssZ)", + "type": "string", + "format": "date-time" + }, + "validUntil":{ + "title": "Valid until", + "description": "The date and time until the credential is valid, formatted in ISO 8601 (YYYY-MM-DDThh:mm:ssZ)", + "type": "string", + "format": "date-time" + }, + "visaNumber":{ + "title": "Visa number", + "description": "The official visa number", + "type": "string" + }, + "passportNumber":{ + "title": "Passport number", + "description": "The number of recipient passport", + "type": "string" + }, + "placeOfIssue":{ + "title":"Place of issue", + "description": "The place of issuance of this credential", + "type": "object", + "x-format": "languageMap", + "patternProperties": { + "^[a-z]{2}$": { + "type": "string", + "description": "A string value for a specific language code." + } + }, + "additionalProperties": false + }, + "nationality":{ + "title":"Nationality", + "description": "Nationality of the recipient of this credential", + "type": "object", + "x-format": "languageMap", + "patternProperties": { + "^[a-z]{2}$": { + "type": "string", + "description": "A string value for a specific language code." + } + }, + "additionalProperties": false + }, + "dateOfBirth":{ + "title": "Date of birth", + "description": "The date and time of recipient birth, formatted in ISO 8601 (YYYY-MM-DDThh:mm:ssZ)", + "type": "string", + "format": "date-time" + }, + "typeOfVisa":{ + "title":"Type of visa", + "description": "Type of visa", + "type": "object", + "x-format": "languageMap", + "patternProperties": { + "^[a-z]{2}$": { + "type": "string", + "description": "A string value for a specific language code." + } + }, + "additionalProperties": false + }, + "entryType":{ + "title":"Entry type", + "description": "Type of entry of recipient", + "type": "object", + "x-format": "languageMap", + "patternProperties": { + "^[a-z]{2}$": { + "type": "string", + "description": "A string value for a specific language code." + } + }, + "additionalProperties": false + } + + }, + "required": ["fullName"] +} \ No newline at end of file diff --git a/visa-certificate/v1/input-fields/translations/en.json b/visa-certificate/v1/input-fields/translations/en.json new file mode 100644 index 0000000..2137a91 --- /dev/null +++ b/visa-certificate/v1/input-fields/translations/en.json @@ -0,0 +1,13 @@ +{ + "fullName": "Full name", + "durationOfStay": "Duration of stay", + "issuanceDate":"Issuance date", + "validUntil": "Valid until", + "visaNumber": "Visa number", + "passportNumber": "Passport number", + "placeOfIssue": "Place of issue", + "nationality": "Nationality", + "dateOfBirth" : "Date of birth", + "typeOfVisa": "Type of visa", + "entryType": "Entry type" +} \ No newline at end of file diff --git a/visa-certificate/v1/input-fields/user-consent/user-consent-map.json b/visa-certificate/v1/input-fields/user-consent/user-consent-map.json new file mode 100644 index 0000000..858fbd0 --- /dev/null +++ b/visa-certificate/v1/input-fields/user-consent/user-consent-map.json @@ -0,0 +1,13 @@ +{ + "fullName": "identity-profile", + "durationOfStay": "credential-info", + "issuanceDate":"credential-info", + "validUntil": "credential-info", + "visaNumber": "credential-info", + "passportNumber": "identity-profile", + "placeOfIssue": "credential-info", + "nationality": "identity-profile", + "dateOfBirth" : "identity-profile", + "typeOfVisa": "credential-info", + "entryType": "credential-info" +} \ No newline at end of file diff --git a/visa-certificate/v1/translations/en.json b/visa-certificate/v1/translations/en.json new file mode 100644 index 0000000..d27d4be --- /dev/null +++ b/visa-certificate/v1/translations/en.json @@ -0,0 +1,5 @@ +{ + "credential": { + "title": "Visa Certificate" + } +} \ No newline at end of file