Gataca-QR
This component built using stencyl allows an easy integration to display a gataca QR.
You only need to install on a front component to scan presentation requests from the (Gataca Wallet)[https://gataca.io/solutions] app.
Installing this component
Script tag
- Put a script tag similar to this
<script src='https://unpkg.com/gatacaqr@1.2.0/dist/gatacaqr.js'></script>
in the head of your index.html - Then you can use the element anywhere in your template, JSX, html etc
Node Modules
- Run
npm install gatacaqr --save
- Put a script tag similar to this
<script src='node_modules/gatacaqr/dist/gatacaqr.js'></script>
in the head of your index.html - Then you can use the element anywhere in your template, JSX, html etc
Styles & Personification
It allows to integrate 2 slots, named "title" and "description", to provide further integration to the user upon display of the QR.
Usage
The goal of this component is to ease the generation and presentation of QRs with Gataca Connect Sessions on any html or frontend project, to allow the integration of GATACA Connect in your own infrastructure or application.
This component should be used with the prerequisite of having an application which can be integrated with Gataca Connect. More precisely, your application will need to be able to perform the two operations against your connect server:
- Create sessions
- Consult sessions
Therefore, in order to make it work, you will need at least:
- A connect server deployed at your infrastructure
- A tenant configured on that Connect and an application with credentials to create sessions on the tenant. You will need the following properties:
- "$YOUR_APP"
- "$YOUR_TENANT"
- "$YOUR_PASSWORD"
- "$YOUR_SERVER"
- An application integrated with that server to perform the basic operations.
To better understand how the QR works, this diagram details its work:
Configurations
To understand all possible configurations. Check the properties section below. Here is a recap of all possible combinations:
- You can choose if to generate a QR used for scanning credentials or to issue new credentials, by setting the qrRole property to scan or credential respectively.
- You need to configure a callbackServer to display on the QR, so that the wallet knows the location of the server to share the data with.
- a. You can use this component with an already created session, which can be inserted on the sessionId property on the element, or passed via query parameter id or sessionId on the current URL.
b. If you want the event of pushing the button to trigger the creation of the session, you can also provide a method to generate a new session providing the
createSession property.
c. If your application matches the gataca-login interface, you can avoid defining your own implementation and use the default implementation just by providing the generationEndpoint.
- a. If you have defined your own service to get the session on the connect, you should implement a checkStatus property. The method needs to return 0, 1 or 2 depending if the credential sharing process is still ongoing, succesfully finished or finished with error respectively.
b. If your application matches the gataca-login interface, you can avoid defining your own implementation and use the default implementation just by providing the sessionEndpoint.
c. You can optionally configure your own frequency for the polling and the session expiration time with the pollingFrequency and sessionTimeout properties.
- To keep the logic on your client-side, your shall configure the sucessCallback and the errorCallback properties, to implement alerts, redirections or further logic upon the ending of the process.
- You can choose to display the component as a button or avoid to display the button and manually trigger the QR using the asButton property. To manually trigger the QR, use javascript to invoke the open() method
- You can choose not display a dynamic link and make the QR lighter by setting the dynamicLink property to false, but it is not recommended for better compatibility and user experience.
To better understand the different relation between properties, this decision diagram explains the relation between them:
You can also check the examples below to check which configuration is more suitable for your case.
Examples
Test connection
The first example to understand how the QR works is to avoid any backend processing (no business logic added); just by pointing the frontend directly to consult a session on the Connect.
Note: Direct integration with Gataca Connect is not recommended and should be used only for demo purposes. There would be credentials leaked on the client side otherwise.
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
<title>Gataca QR Component</title>
<script src='https://unpkg.com/gatacaqr@1.2.0/dist/gatacaqr.js'></script>
<style type="text/css">
.qrTitle {
color: #181B5E;
align-self: center;
text-align: center;
}
.qrDesc {
color: #181B5E;
}
</style>
</head>
<body>
<gataca-qr
id="gataca-qr"
callback-server="https://connect.dev.gatacaid.com:9090"
session-endpoint="http://localhost:9009/login?id="
qr-modal-title="Easy login"
qr-modal-description="Scan this QR to open your gataca wallet"
button-text="Easy login">
<script>
const qr = document.getElementById('gataca-qr');
const qrTitle = document.getElementById('qrTitle');
const RESULT_STATUS = {
ONGOING: 0,
SUCCESS: 1,
FAILED: 2,
}
let appToken = "";
let sessionToken = "";
const appName = "$YOUR_APP";
const tenant = "$YOUR_TENANT";
const appPassword = "$YOUR_PASSWORD";
const connectServer = "$YOUR_SERVER";
qr.callbackServer = connectServer;
const getAppToken = async () => {
if (!appToken) {
let response = await fetch(
connectServer + "/admin/v1/login/basic",
{
method: "POST",
headers: {
'Content-Type': 'application/json',
'Tenant': tenant,
'Authorization': 'Basic ' + btoa(appName + ":" + appPassword)
},
body: "{}"
})
appToken = response.headers.get('Token')
return appToken
}
return appToken
}
qrTitle.onClick = (e) => {
qr.open = false;
}
const processData = (data) => {
let result = {}
for (let vc of data?.verifiableCredential) {
for (let key of Object.keys(vc.credentialSubject)) {
if (key != "id") {
result[key] = vc.credentialSubject[key]
}
}
}
return result
}
qr.createSession = async () => {
let endpoint = connectServer + "/api/v1/sessions";
let response = await fetch(
endpoint,
{
method: "POST",
headers: {
'Content-Type': 'application/json',
'Authorization': 'jwt ' + await getAppToken()
},
body: "{}"
})
let data = await response.json();
sessionToken = response.headers.get('Token');
return data.id
}
qr.checkStatus = async (id) => {
let endpoint = connectServer + '/api/v1/sessions/' + id;
let response = await fetch(
endpoint,
{
headers: {
'Content-Type': 'application/json',
'Authorization': 'jwt ' + sessionToken,
}
}
);
let data = response.status === 200 ? (await response.json()).data : null;
if (data) {
qr.sessionData = processData(data)
}
return response.status === 200 ? RESULT_STATUS.SUCCESS : response.status === 204 ? RESULT_STATUS.ONGOING : RESULT_STATUS.FAILED;
}
qr.successCallback = (data, token) => {
alert("LOGIN OK: " + qr.sessionData["some_info_field_you_requested"])
};
qr.errorCallback = () => {
alert("Wrong credentials!")
};
</script>
</body>
</html>
HTML Only with the standard interface
This example shows how to integrate the QR Component on a normal scenario, on the easiest case: the application server exposes the same interface as the Access API on the GATACA Connect.
To understand the GATACA Connect Access API, this diagram could help:
As you can see from the diagram, the Access API performs an internal call to the sessions API to generate and validate sessions. The retrieved information is then mapped with our business logic to perform authorization and generate tokens if needed.
The definition of the default interface is can be found on the Gataca Connect documentation:
The first request - the gataca login request- is the default session generation endpoint, whereas the second one -Gataca Login- Post response is the session consultation endpoint.
App Service Exposed | Connect Service Consumed |
---|
Login Request | Create Session |
Login Gataca | Get Session Data |
The pseudo-code of those functions in the case of the AccessAPI, that could easily be adapted to your needs, is the following:
func loginRequest(request, response) {
sessionID, token = connectService.CreateSession(session)
cipheredToken = cipher(token)
response.Header.Set("connect_token", token)
response.Header.Set("session_id", sessionID)
response = {
"session_id": sessionID,
"connect_token": connectToken,
}
return response.writeJSON(http.StatusOK, response)
}
func loginGataca(request, response) error {
sessionID = request.Header.Get("session_id")
connectToken = request.Header.Get("connect_token")
decipheredToken = decipher(token)
data, finish_code = connectService.GetSessionData(session, token)
if (finish_code = 204) {
return response.writeJSON(http.StatusPreconditionRequired,"")
} else if (finish_code = 200) {
dataProcessed = extractData(data)
token = validateDataAndAuthenticate(dataProcessed)
response.setToken(token)
return response.writeJSON(http.StatusOK, {"data": dataProcessed})
} else {
return response.writeJSON(http.StatusError, "")
}
}
Note: The default values provided for demo will always result in rejected sessions, as it is controlled by the gataca access application logic: the authentication using verifiable credentials will be successful whereas the authorization against our business logic will be rejected.
The example configuration of the QR if your application implements the standard API is very simple:
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
<title>Gataca QR Component</title>
<script src='https://unpkg.com/gatacaqr@1.2.0/dist/gatacaqr.js'></script>
<style type="text/css">
.qrTitle {
color: #181B5E;
align-self: center;
text-align: center;
}
.qrDescription {
color: #181B5E;
}
</style>
</head>
<body>
<gataca-qr
id="gataca-qr"
session-timeout="300"
polling-frequency="3"
generation-endpoint ='https://connect.gataca.io:9090/admin/v1/login/request'
session-endpoint = "https://connect.gataca.io:9090/admin/v1/login/gataca"/>
<script>
const qr = document.getElementById('gataca-qr');
const qrTitle = document.getElementById('qrTitle');
qrTitle.onClick = (e) => {
qr.open = false;
}
qr.successCallback = (data, token) => {
alert("LOGIN OK: " + data)
};
qr.errorCallback = () => {
alert("Wrong credentials!")
};
</script>
</body>
</html>
___ Note: ___ The following examples explain how to use certain configuration parameters depending on your application needs
You can use this component with an already created session, which can be inserted on the sessionId property on the element, or passed via query parameter id or sessionId on the current URL.
You can also provide a method to generate a new session like in the example, or, in the rare event of matching the authorizer API, just the endpoint to your application.
Application rendering HTML Only
This example shows how to integrate the QR Component on a normal scenario, where the application defines its own interface for the services.
You can find an example of that kind of simple application (written in Go) on the Gataca Authorizer, which we will use as example to explain the component's usage.
Gataca Authorizer creates the sessionId before rendering the page and provides it as a parameter on the HTML template. To query the status of the session, it offers the following endpoint:
- /login/status : Check the status of the created session
Continuing with the example as before, you could integrate with that kind of application - running at $APP_DOMAIN- by modifying the following pieces of code
- Add JQuery Library in HEAD
<head>
...
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
...
</head>
- Modify the parameters of the QR to present the connect server and the session id renderized on the HTML template when loaded by the backend.
<body>
...
<gataca-qr id="gataca-qr" callback-server="{{.ConnectServer}}" session-id="{{.SessionId}}">
...
</body>
- Include a checkStatus implementation to retrieve the session data.
<script>
const qr = document.getElementById('gataca-qr');
....
qr.checkStatus = async (id) => {
console.log("Check status called for session", id)
try {
response = await $.get("login/status")
console.log("Got Status Response", response)
qr.sessionData = (await response.json()).data;
return 1;
} catch (err) {
console.log("Error", err)
switch (err.status) {
case 428:
return 0;
default:
return 2
}
}
}
...
</script>
Note: If you want to retrieve the data on the successCallback, you need to store on the checkStatus invocation the session data in the sessionData property from the qr
Note 2: If Gataca Authorizer also had an endpoint to generate the sessionId, and in case we wanted that the sessionId is not generated before serving the HTML file, but when clicking the button, we could do it just by modifying the previous example:
- Modify the parameters of the QR to remove the session-id parameter.
<body>
...
<gataca-qr id="gataca-qr" callback-server="{{.ConnectServer}}">
...
</body>
- Include a createSession implementation to retrieve the session data.
<script>
const qr = document.getElementById('gataca-qr');
....
qr.createSession = async (id) => {
}
...
</script>
SPA React Application
In this example, we will show how to integrate it with a full react application.
- Run
npm install gatacaqr --save
- In index.js, include the following code at the beggining:
import { applyPolyfills, defineCustomElements } from 'gatacaqr/loader';
applyPolyfills().then(() => {
defineCustomElements(window);
});
- Create a JS component to use the QR or just invoke it from your component:
import React, { useEffect, useState, useRef } from 'react'
export const QRLogin: React.FC = () => {
const qr = useRef(null)
const gatacaLoginSuccess = (data: any, token: string) => {
alert('Gataca Login Successful')
}
const gatacaLoginError = error => {
alert('Gataca Login Error')
}
useEffect(() => {
qr.current.generationEndpoint =
process.env.REACT_APP_CONNECT_HOST + '/admin/v1/login/request'
qr.current.sessionEndpoint =
process.env.REACT_APP_CONNECT_HOST + '/admin/v1/login/gataca'
qr.current.successCallback = gatacaLoginSuccess
qr.current.errorCallback = gatacaLoginError
}, [username, password])
return (<gataca-qr
callback-server={process.env.REACT_APP_CONNECT_HOST}
ref={qr}
polling-frequency="15"/>
)
}
In order to consult sessions, both options are also available, depending on how you want to develop your own API: either checkStatus method or the sessionEndpoint if your API matches the expecter authorizer API.
Properties
Property | Attribute | Description | Type | Default |
---|
sessionId | session-id | [Optional] Generated session Id, which is required. Without session Id, the QR will not work. If the property is unset, it will check for an id or sessionId query parameter on the current URL. If there is no sessionId, it will fallback to the createSession method to generate a new Session.↓ | string | - |
createSession | -- | [Optional] Create session function to generate a new Session If the property is unset, it will fallback to the generation Endpoint property.↓ | () => Promise<string> | undefined |
generationEndpoint | generation-endpoint | [Optional] Session Generation URL to create a new Session. It will expect to receive the session Id from the response header 'X-Connect-Id'. If not set, it would use a default endpoint to the same window URL under the path /auth | string | https://$DOMAIN:9090/admin/v1/login/request |
checkStatus | -- | [Optional] Check status function to query the current status of the session If not set, it would fallback to the session Endpoint property. ↓ | (id?: string) => Promise<RESULT_STATUS> | undefined |
sessionEndpoint | session-endpoint | [Optional] EndpointURL to fetch data for the status. The endpoint URL will send a GET request with the session id on a parameter; concatenated to this string. It can be used if your API fulfills the requirement. If not, use the checkStatus property. If not set, it would use a default endpoint to the same window URL under the path /auth | string | https://$CURRENT_DOMAIN:9090/admin/v1/login/gataca |
successCallback | -- | Mandatory Callback fired upon session correctly verified If not set, session validation wouldn't trigger any action The session data and a possible token will be sent as parameters to the callback | (data?: any, token?: string) => void | - |
errorCallback | -- | Mandatory Callback fired upon session expired or invalid If not set, session error would not be handled An error containing information will be passed as parameter | (error?: Error) => void | - |
callbackServer | callback-server | Mandatory Connect Server where the wallet will send the data | string | https://connect.gatacaid.com:9090 |
pollingFrequency | polling-frequency | [Optional] Frequency in seconds to check if the session has been validated | number | 3 |
sessionTimeout | session-timeout | [Optional] Maximum time window to display the session | number | 180 |
dynamicLink | dynamic-link | [Optional] Display a link containing a dynamic link to invoke the wallet if closed | boolean | true |
qrRole | qr-role | [Optional] Decide if scanning the credential as a verifier to request credentials or as an issuer too issue credentials. Options: scan (default) | credential | string | scan |
asButton | as-button | [Optional] Decide if to show it as a button to display the QR Or display directly the QR. Default: true (display button) | boolean | true |
qrModalTitle | qr-modal-title | [Optional] Text that is shown to personalise the message of the qr modal. | string | |
qrModalDescription | qr-modal-description | [Optional] Text to personalise the qr modal description to explain the user what need to be done with the QR code. | string | |
buttonText | button-text | [Optional] Text of the button. | string | |
Events
Event | Description | Type |
---|
gatacaLoginCompleted | GatacaLoginCompleted event, triggered with session data upon login success | CustomEvent<any> |
gatacaLoginFailed | GatacaLoginFailed event, triggered with error upon login failure | CustomEvent<any> |
Methods
display() => Promise<void>
Force manually the display of a QR
Returns
Type: Promise<void>
getSessionData() => Promise<any>
Retrieve manually the session data on a successful login
Returns
Type: Promise<any>
getToken() => Promise<string>
Retrieve manually a possible token retrieved upon login on the Header 'token'
Returns
Type: Promise<string>
stop() => Promise<void>
Stop manually an ongoing session
Returns
Type: Promise<void>
Javascript invocation
You can display or hide the displayed QR by setting it's open state property.
Overriding styles
TBD...
Built with StencilJS