diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..18fd272 --- /dev/null +++ b/src/api.ts @@ -0,0 +1,69 @@ +import { CorsignPayload, CorsignToken } from './token'; + +const corsignApiUrl = 'https://corsign.de/v1'; + +export interface GenerateSignedCorsignTokenResponse { + /** + * Encoded Corsign-JWT + */ + token: string; + + /** + * QRCode Data URI + */ + qrCode: string; +} + +/** + * + * @param payload The payload for the Corsign-Token + * @param signerToken + * @param tokenId + * @param apiUrl + * @returns Encoded Token and QRCode {@link GenerateSignedCorsignTokenResponse} + */ +export const generateSignedCorsignToken = async ( + payload: CorsignPayload, + signerToken: string, + tokenId: string, + apiUrl: string = corsignApiUrl +): Promise => { + const response = await fetch(`${apiUrl}/sign`, { + method: 'POST', + headers: { + 'X-SIGNER-TOKEN': signerToken, + 'X-TOKEN-ID': tokenId, + 'content-type': 'application/json;charset=UTF-8', + }, + body: JSON.stringify(payload), + }); + + const { data, errors } = await response.json(); + + if (response.ok) { + return data as GenerateSignedCorsignTokenResponse; + } else { + return Promise.reject(errors); + } +}; + +/** + * + * @param token Encoded Corsign-JWT + * @param apiUrl + * @returns Decoded ({@link CorsignToken}) if successfull + */ +export const validateCorsignToken = async ( + token: string, + apiUrl: string = corsignApiUrl +): Promise => { + const response = await fetch(`${apiUrl}/validate/${token}`); + + const { data, errors } = await response.json(); + + if (response.ok) { + return data as CorsignToken; + } else { + return Promise.reject(errors); + } +}; diff --git a/src/index.ts b/src/index.ts index 6b36029..4ccfa9a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,19 @@ -export * from './token'; +export type { + CorsignPayload, + CorsignPayloadInformation, + CorsignPayloadPerson, + CorsignToken, +} from './token'; + +export { + decodeCorsignToken, + generateCorsignQrCode, + generateUnsignedCorsignToken, +} from './utils'; + +export type { GenerateSignedCorsignTokenResponse } from './api'; + +export { + generateSignedCorsignToken, + validateCorsignToken, +} from './api'; diff --git a/src/token.ts b/src/token.ts index 74df19e..8e1c860 100644 --- a/src/token.ts +++ b/src/token.ts @@ -9,18 +9,24 @@ export type CorsignToken = { /** * The token expires after a pre-defined duration (e.g 24 hours) passed since the Sars-CoV-2 was done + * + * UTC timestamp */ - exp?: string; + exp?: number; /** - * Date and time of the Sars-Cov-2 test + * Date and time of the Sars-Cov-2 test **or** for unsigned tokens date of creation + * + * UTC timestamp */ - iat?: string; + iat?: number; /** * Valid not before Sars-Cov-2 test date and time + * + * UTC timestamp */ - nbf?: string; + nbf?: number; /** * Issuer of this Token @@ -173,7 +179,12 @@ export type CorsignPayloadInformation = { vaccine?: 'BNT162b2' | 'mRNA-1273' | string; /** - * Additional third-party app data + * Additional third-party app data 1 + */ + appData1?: Record; + + /** + * Additional third-party app data 2 */ - appData?: Record; + appData2?: Record; }; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..4ed8179 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,48 @@ +import { CorsignPayloadPerson, CorsignToken } from './token'; +import jwt from 'jsonwebtoken'; +import QRCode from 'qrcode'; + +const corsignValidationUrl = 'https://corsign.de/token'; + +/** + * + * @param person Person payload + * @param issuer Issuer + * @param privateKey Optional private key + * @returns Corsign-JWT + */ +export const generateUnsignedCorsignToken = ( + person: CorsignPayloadPerson, + issuer: string, + privateKey: string = 'self' +): string => { + const token: CorsignToken = { + iss: issuer, + aud: 'self', + pld: { person }, + }; + return jwt.sign(token, privateKey); +}; + +/** + * Decodes Encoded Corsign-JWT **without validation** + * + * @param token Encoded Corsign-JWT + * @returns Decoded {@link CorsignToken} + */ +export const decodeCorsignToken = (token: string): CorsignToken => + jwt.decode(token) as CorsignToken; + +/** + * + * @param token Any {@link CorsignToken} + * @param validationUrl The url the qr code should point to defaults to https://corsign.de/token/{token} + * @returns Data URI containing a representation of the QR Code image. + */ +export const generateCorsignQrCode = async ( + token: string, + validationUrl: string = corsignValidationUrl +): Promise => { + const code = QRCode.create(`${validationUrl}/${token}`, {}); + return QRCode.toDataURL(code.segments); +};