commit 0ddde64f1d10c4b2e7fc02a14f8bc31db97219eb parent 723cf257e7a3e644cc214e4bb359d9ce8ba8d7ad Author: Henrique Chan Carvalho Machado <henriqueccmachado@tecnico.ulisboa.pt> Date: Sat, 24 Jan 2026 20:01:04 +0100 Fix kych references in docs Diffstat:
12 files changed, 1423 insertions(+), 213 deletions(-)
diff --git a/documentation/sequence_diagrams/authorize_sequence.txt b/documentation/sequence_diagrams/authorize_sequence.txt @@ -1,47 +1,47 @@ sequenceDiagram participant Client - participant Kych oauth2 Gateway - participant Kych oauth2 Gateway DB + participant KyCH OAuth2 Gateway + participant KyCH OAuth2 Gateway DB participant Swiyu Verifier - Client ->> Kych oauth2 Gateway: GET /authorize/{nonce}?\nresponse_type=code&\nclient_id={client_id}&\nredirect_uri={redirect_uri}&\nstate={state}&\nscope={scope} + Client ->> KyCH OAuth2 Gateway: GET /authorize/{nonce}?\nresponse_type=code&\nclient_id={client_id}&\nredirect_uri={redirect_uri}&\nstate={state}&\nscope={scope} - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Validate parameters:\n- response_type == 'code' + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Validate parameters:\n- response_type == 'code' alt Invalid parameters - Kych oauth2 Gateway -->> Client: 400 BAD REQUEST\n{error: 'invalid_request'} + KyCH OAuth2 Gateway -->> Client: 400 BAD REQUEST\n{error: 'invalid_request'} else Valid parameters - Kych oauth2 Gateway ->> Kych oauth2 Gateway DB: SELECT s.id, s.status, s.expires_at,\ns.scope, s.verification_url, s.request_id,\ns.verification_deeplink, c.verifier_url,\nc.verifier_management_api_path,\nc.allowed_redirect_uris, c.accepted_issuer_dids\nFROM verification_sessions s\nJOIN clients c ON s.client_id = c.id\nWHERE s.nonce = $1 AND c.client_id = $2 + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway DB: SELECT s.id, s.status, s.expires_at,\ns.scope, s.verification_url, s.request_id,\ns.verification_deeplink, c.verifier_url,\nc.verifier_management_api_path,\nc.allowed_redirect_uris, c.accepted_issuer_dids\nFROM verification_sessions s\nJOIN clients c ON s.client_id = c.id\nWHERE s.nonce = $1 AND c.client_id = $2 - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: Query result + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: Query result end alt Session not found - Kych oauth2 Gateway -->> Client: 404 NOT FOUND\n{error: 'session_not_found'} + KyCH OAuth2 Gateway -->> Client: 404 NOT FOUND\n{error: 'session_not_found'} else Invalid redirect_uri - Kych oauth2 Gateway -->> Client: 400 BAD REQUEST\n{error: 'invalid_redirect_uri'} + KyCH OAuth2 Gateway -->> Client: 400 BAD REQUEST\n{error: 'invalid_redirect_uri'} else Session expired - Kych oauth2 Gateway -->> Client: 410 GONE\n{error: 'session_expired'} + KyCH OAuth2 Gateway -->> Client: 410 GONE\n{error: 'session_expired'} else Session not pending (already processed) - Kych oauth2 Gateway -->> Client: 409 CONFLICT\n{error: 'invalid_session_status'} + KyCH OAuth2 Gateway -->> Client: 409 CONFLICT\n{error: 'invalid_session_status'} else Session already authorized (idempotent) - Kych oauth2 Gateway -->> Client: 200 OK\n{verification_id, verification_url,\nverification_deeplink, state} + KyCH OAuth2 Gateway -->> Client: 200 OK\n{verification_id, verification_url,\nverification_deeplink, state} else Session valid and pending - proceed - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Validate scope against allowed_scopes\nbuild_presentation_definition(scope) + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Validate scope against allowed_scopes\nbuild_presentation_definition(scope) - Kych oauth2 Gateway ->> Swiyu Verifier: POST /management/api/verifications\n{presentation_definition, response_mode,\naccepted_issuer_dids, ...} - Swiyu Verifier -->> Kych oauth2 Gateway: Response + KyCH OAuth2 Gateway ->> Swiyu Verifier: POST /management/api/verifications\n{presentation_definition, response_mode,\naccepted_issuer_dids, ...} + Swiyu Verifier -->> KyCH OAuth2 Gateway: Response alt Verifier error - Kych oauth2 Gateway -->> Client: 502 BAD GATEWAY\n{error: 'verifier_error'} + KyCH OAuth2 Gateway -->> Client: 502 BAD GATEWAY\n{error: 'verifier_error'} else Success - Kych oauth2 Gateway ->> Kych oauth2 Gateway DB: UPDATE verification_sessions\nSET verification_url = $1, request_id = $2,\nverification_deeplink = $3, status = 'authorized',\nauthorized_at = NOW(), scope = $4,\nredirect_uri = $5, state = $6\nWHERE id = $7\nRETURNING verification_url, request_id + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway DB: UPDATE verification_sessions\nSET verification_url = $1, request_id = $2,\nverification_deeplink = $3, status = 'authorized',\nauthorized_at = NOW(), scope = $4,\nredirect_uri = $5, state = $6\nWHERE id = $7\nRETURNING verification_url, request_id alt DB error - Kych oauth2 Gateway -->> Client: 500 INTERNAL SERVER ERROR + KyCH OAuth2 Gateway -->> Client: 500 INTERNAL SERVER ERROR else Success - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: Updated session - Kych oauth2 Gateway -->> Client: 200 OK\n{verification_id, verification_url,\nverification_deeplink, state} + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: Updated session + KyCH OAuth2 Gateway -->> Client: 200 OK\n{verification_id, verification_url,\nverification_deeplink, state} end end end diff --git a/documentation/sequence_diagrams/info_sequence.txt b/documentation/sequence_diagrams/info_sequence.txt @@ -1,29 +1,29 @@ sequenceDiagram participant Client - participant Kych oauth2 Gateway - participant Kych oauth2 Gateway DB + participant KyCH OAuth2 Gateway + participant KyCH OAuth2 Gateway DB - Client ->> Kych oauth2 Gateway: GET /info \nAuthorization: Bearer <token> + Client ->> KyCH OAuth2 Gateway: GET /info \nAuthorization: Bearer <token> - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Extract token from \nAuthorization header + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Extract token from \nAuthorization header alt Missing or malformed Authorization header - Kych oauth2 Gateway -->> Client: 401 UNAUTHORIZED \n{error: 'invalid_token'} + KyCH OAuth2 Gateway -->> Client: 401 UNAUTHORIZED \n{error: 'invalid_token'} else Valid header format - Kych oauth2 Gateway ->> Kych oauth2 Gateway DB: UPDATE access_tokens t \nSET revoked = t.revoked \nFROM verification_sessions s \nWHERE t.session_id = s.id \nAND t.token = $1 \nAND t.expires_at > NOW() \nRETURNING t.revoked, s.status, \ns.verifiable_credential + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway DB: UPDATE access_tokens t \nSET revoked = t.revoked \nFROM verification_sessions s \nWHERE t.session_id = s.id \nAND t.token = $1 \nAND t.expires_at > NOW() \nRETURNING t.revoked, s.status, \ns.verifiable_credential alt Token not found or expired - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: 0 rows - Kych oauth2 Gateway -->> Client: 401 UNAUTHORIZED \n{error: 'invalid_token'} + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: 0 rows + KyCH OAuth2 Gateway -->> Client: 401 UNAUTHORIZED \n{error: 'invalid_token'} else Token found - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: token and session data + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: token and session data - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Validate:\n- not revoked\n- status == 'completed' + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Validate:\n- not revoked\n- status == 'completed' alt Invalid token state - Kych oauth2 Gateway -->> Client: 401 UNAUTHORIZED \n{error: 'invalid_token'} + KyCH OAuth2 Gateway -->> Client: 401 UNAUTHORIZED \n{error: 'invalid_token'} else Valid token and VC available - Kych oauth2 Gateway -->> Client: 200 OK \n{verifiable_credential} + KyCH OAuth2 Gateway -->> Client: 200 OK \n{verifiable_credential} end end end diff --git a/documentation/sequence_diagrams/notification_sequence.txt b/documentation/sequence_diagrams/notification_sequence.txt @@ -1,45 +1,45 @@ sequenceDiagram participant Swiyu Verifier - participant Kych oauth2 Gateway - participant Kych oauth2 Gateway DB + participant KyCH OAuth2 Gateway + participant KyCH OAuth2 Gateway DB - note over Swiyu Verifier,Kych oauth2 Gateway DB: Incoming Webhook from Swiyu + note over Swiyu Verifier,KyCH OAuth2 Gateway DB: Incoming Webhook from Swiyu - Swiyu Verifier ->> Kych oauth2 Gateway: POST /notification \n{verification_id, timestamp} + Swiyu Verifier ->> KyCH OAuth2 Gateway: POST /notification \n{verification_id, timestamp} - Kych oauth2 Gateway ->> Kych oauth2 Gateway DB: UPDATE verification_sessions s \nSET status = s.status \nFROM clients c \nWHERE s.client_id = c.id \nAND s.request_id = $1 \nRETURNING s.id, s.nonce, s.status, \nc.id AS client_id, c.webhook_url, \nc.verifier_url, c.verifier_management_api_path + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway DB: UPDATE verification_sessions s \nSET status = s.status \nFROM clients c \nWHERE s.client_id = c.id \nAND s.request_id = $1 \nRETURNING s.id, s.nonce, s.status, \nc.id AS client_id, c.webhook_url, \nc.verifier_url, c.verifier_management_api_path alt DB error or session invalid - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: Error / 0 rows - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Log error\n- DB connection failed\n- Session not found\n- Session not authorized\n- Session already processed - Kych oauth2 Gateway -->> Swiyu Verifier: 200 OK + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: Error / 0 rows + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Log error\n- DB connection failed\n- Session not found\n- Session not authorized\n- Session already processed + KyCH OAuth2 Gateway -->> Swiyu Verifier: 200 OK else Session found - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: session + client data + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: session + client data - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Validate session (status == 'authorized') + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Validate session (status == 'authorized') alt Session invalid - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Log error\n- Session not authorized\n- Session already processed - Kych oauth2 Gateway -->> Swiyu Verifier: 200 OK + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Log error\n- Session not authorized\n- Session already processed + KyCH OAuth2 Gateway -->> Swiyu Verifier: 200 OK else Session valid - Kych oauth2 Gateway ->> Swiyu Verifier: GET verifier_url + verifier_management_api_path + /verification_id - Swiyu Verifier -->> Kych oauth2 Gateway: {state: 'Success'/'Failed'/'Pending', wallet_response} + KyCH OAuth2 Gateway ->> Swiyu Verifier: GET verifier_url + verifier_management_api_path + /verification_id + Swiyu Verifier -->> KyCH OAuth2 Gateway: {state: 'Success'/'Failed'/'Pending', wallet_response} alt Verification pending - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Log info, ignore webhook - Kych oauth2 Gateway -->> Swiyu Verifier: 200 OK + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Log info, ignore webhook + KyCH OAuth2 Gateway -->> Swiyu Verifier: 200 OK else Verification success or failed - Kych oauth2 Gateway ->> Kych oauth2 Gateway: generate_authorization_code() + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: generate_authorization_code() - Kych oauth2 Gateway ->> Kych oauth2 Gateway DB: WITH updated_session AS (\n UPDATE verification_sessions \n SET status = $1, verified_at = NOW(),\n verifiable_credential = $5 \n WHERE id = $2 RETURNING id\n)\nINSERT INTO authorization_codes \n(session_id, code, expires_at) \nVALUES ($2, $3, NOW() + INTERVAL '10 minutes') \nRETURNING code + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway DB: WITH updated_session AS (\n UPDATE verification_sessions \n SET status = $1, verified_at = NOW(),\n verifiable_credential = $5 \n WHERE id = $2 RETURNING id\n)\nINSERT INTO authorization_codes \n(session_id, code, expires_at) \nVALUES ($2, $3, NOW() + INTERVAL '10 minutes') \nRETURNING code alt Operation failed - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Log error\n- Verifier fetch failed\n- DB update failed\n- Code generation failed + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Log error\n- Verifier fetch failed\n- DB update failed\n- Code generation failed else Success - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: authorization_code - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Log success + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: authorization_code + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Log success end - Kych oauth2 Gateway -->> Swiyu Verifier: 200 OK + KyCH OAuth2 Gateway -->> Swiyu Verifier: 200 OK end end end diff --git a/documentation/sequence_diagrams/setup_sequence.txt b/documentation/sequence_diagrams/setup_sequence.txt @@ -1,32 +1,32 @@ sequenceDiagram participant Client - participant Kych oauth2 Gateway - participant Kych oauth2 Gateway DB + participant KyCH OAuth2 Gateway + participant KyCH OAuth2 Gateway DB - Client ->> Kych oauth2 Gateway: POST /setup/{client_id}\nAuthorization: Bearer {client_secret} + Client ->> KyCH OAuth2 Gateway: POST /setup/{client_id}\nAuthorization: Bearer {client_secret} - Kych oauth2 Gateway ->> Kych oauth2 Gateway DB: SELECT secret_hash FROM clients\nWHERE client_id = $1 + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway DB: SELECT secret_hash FROM clients\nWHERE client_id = $1 alt Client not found - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: 0 rows - Kych oauth2 Gateway -->> Client: 401 UNAUTHORIZED\n{error: "unauthorized"} + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: 0 rows + KyCH OAuth2 Gateway -->> Client: 401 UNAUTHORIZED\n{error: "unauthorized"} else Client found - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: secret_hash - Kych oauth2 Gateway ->> Kych oauth2 Gateway: bcrypt_verify(bearer_token, secret_hash) + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: secret_hash + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: bcrypt_verify(bearer_token, secret_hash) alt Invalid secret - Kych oauth2 Gateway -->> Client: 401 UNAUTHORIZED\n{error: "unauthorized"} + KyCH OAuth2 Gateway -->> Client: 401 UNAUTHORIZED\n{error: "unauthorized"} else Valid secret - Kych oauth2 Gateway ->> Kych oauth2 Gateway: generate_nonce()\n(256-bit CSPRNG) + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: generate_nonce()\n(256-bit CSPRNG) - Kych oauth2 Gateway ->> Kych oauth2 Gateway DB: INSERT INTO verification_sessions\n(client_id, nonce, expires_at)\nSELECT c.id, $1, NOW() + INTERVAL '15 minutes'\nFROM clients c WHERE c.client_id = $2\nRETURNING id, nonce, expires_at + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway DB: INSERT INTO verification_sessions\n(client_id, nonce, expires_at)\nSELECT c.id, $1, NOW() + INTERVAL '15 minutes'\nFROM clients c WHERE c.client_id = $2\nRETURNING id, nonce, expires_at alt DB error - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: Error - Kych oauth2 Gateway -->> Client: 500 INTERNAL SERVER ERROR + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: Error + KyCH OAuth2 Gateway -->> Client: 500 INTERNAL SERVER ERROR else Success - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: session {id, nonce, expires_at} - Kych oauth2 Gateway -->> Client: 200 OK {nonce} + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: session {id, nonce, expires_at} + KyCH OAuth2 Gateway -->> Client: 200 OK {nonce} end end end diff --git a/documentation/sequence_diagrams/swiyu_taler_sequence_diagram.txt b/documentation/sequence_diagrams/swiyu_taler_sequence_diagram.txt @@ -4,7 +4,7 @@ sequenceDiagram participant Browser participant TalerWallet participant Exchange - participant Kych oauth2 Gateway + participant KyCH OAuth2 Gateway participant SwiyuVerifier participant SwiyuWallet @@ -13,20 +13,20 @@ sequenceDiagram TalerWallet ->> Browser: Open link Browser ->> Exchange: Select verification method (Swiyu) - note over Exchange,Kych oauth2 Gateway: Exchange initiates KYC verification process - Exchange ->> Kych oauth2 Gateway: POST /setup/{client_id}\nAuthorization: Bearer $CLIENT_SECRET - Kych oauth2 Gateway -->> Exchange: {nonce: $NONCE} + note over Exchange,KyCH OAuth2 Gateway: Exchange initiates KYC verification process + Exchange ->> KyCH OAuth2 Gateway: POST /setup/{client_id}\nAuthorization: Bearer $CLIENT_SECRET + KyCH OAuth2 Gateway -->> Exchange: {nonce: $NONCE} Exchange ->> Browser: Displays /authorize link to user - Browser ->> Kych oauth2 Gateway: GET /authorize/{nonce}\n?response_type=code\n&client_id={client_id}\n&redirect_uri={redirect_uri}\n&state={state}\n&scope={scope} - Kych oauth2 Gateway ->> SwiyuVerifier: POST /management/api/verifications\n(Creates verification request for {scope}) - SwiyuVerifier -->> Kych oauth2 Gateway: {verification_url, id, state: PENDING} - Kych oauth2 Gateway -->> Browser: HTML page\n(verification_url, verification_id, state)\nQR code + swiyu deeplink encode verification_url + Browser ->> KyCH OAuth2 Gateway: GET /authorize/{nonce}\n?response_type=code\n&client_id={client_id}\n&redirect_uri={redirect_uri}\n&state={state}\n&scope={scope} + KyCH OAuth2 Gateway ->> SwiyuVerifier: POST /management/api/verifications\n(Creates verification request for {scope}) + SwiyuVerifier -->> KyCH OAuth2 Gateway: {verification_url, id, state: PENDING} + KyCH OAuth2 Gateway -->> Browser: HTML page\n(verification_url, verification_id, state)\nQR code + swiyu deeplink encode verification_url loop Poll until status is "verified" or "failed" - Browser ->> Kych oauth2 Gateway: GET /status/{verification_id}\n?state={state} - Kych oauth2 Gateway -->> Browser: {status: "pending" | "authorized"} + Browser ->> KyCH OAuth2 Gateway: GET /status/{verification_id}\n?state={state} + KyCH OAuth2 Gateway -->> Browser: {status: "pending" | "authorized"} end Browser ->> SwiyuWallet: Open $VERIFICATION_URL\n(scan QR or open swiyu wallet deeplink) @@ -37,24 +37,24 @@ sequenceDiagram SwiyuWallet ->> SwiyuWallet: User grants permission SwiyuWallet ->> SwiyuVerifier: POST /oid4vp/api/request-object/{request_id}/response-data\n(VP Token) - note over Kych oauth2 Gateway,SwiyuVerifier: Kych oauth2 Gateway receives webhook\nand retrieves wallet response - SwiyuVerifier ->> Kych oauth2 Gateway: POST /notification\n{verification_id, timestamp} - Kych oauth2 Gateway ->> SwiyuVerifier: GET /management/api/verifications/{verification_id} - SwiyuVerifier -->> Kych oauth2 Gateway: {status: SUCCESS/FAILED,\nwallet_response} + note over KyCH OAuth2 Gateway,SwiyuVerifier: KyCH OAuth2 Gateway receives webhook\nand retrieves wallet response + SwiyuVerifier ->> KyCH OAuth2 Gateway: POST /notification\n{verification_id, timestamp} + KyCH OAuth2 Gateway ->> SwiyuVerifier: GET /management/api/verifications/{verification_id} + SwiyuVerifier -->> KyCH OAuth2 Gateway: {status: SUCCESS/FAILED,\nwallet_response} - note over Browser,Kych oauth2 Gateway: /authorize page detects completion and redirects - Browser ->> Kych oauth2 Gateway: GET /status/{verification_id}\n?state={state} - Kych oauth2 Gateway -->> Browser: {status: "verified"} + note over Browser,KyCH OAuth2 Gateway: /authorize page detects completion and redirects + Browser ->> KyCH OAuth2 Gateway: GET /status/{verification_id}\n?state={state} + KyCH OAuth2 Gateway -->> Browser: {status: "verified"} Browser ->> Browser: /authorize JS redirects to /finalize - Browser ->> Kych oauth2 Gateway: GET /finalize/{verification_id}\n?state={state} - Kych oauth2 Gateway -->> Browser: HTTP 302 Redirect\nLocation: {redirect_uri}?code={auth_code}&state={state} + Browser ->> KyCH OAuth2 Gateway: GET /finalize/{verification_id}\n?state={state} + KyCH OAuth2 Gateway -->> Browser: HTTP 302 Redirect\nLocation: {redirect_uri}?code={auth_code}&state={state} Browser ->> Exchange: GET {redirect_uri}\n?code={auth_code}\n&state={state} - note over Exchange,Kych oauth2 Gateway: Exchange retrieves the Verifiable Credential - Exchange ->> Kych oauth2 Gateway: POST /token\nContent-Type: application/x-www-form-urlencoded\ngrant_type=authorization_code\n&code={auth_code}\n&client_id={client_id}\n&client_secret={client_secret} - Kych oauth2 Gateway -->> Exchange: {access_token,\ntoken_type: "Bearer",\nexpires_in} - Exchange ->> Kych oauth2 Gateway: GET /info\nAuthorization: Bearer $ACCESS_TOKEN - Kych oauth2 Gateway -->> Exchange: $VERIFIABLE_CREDENTIAL + note over Exchange,KyCH OAuth2 Gateway: Exchange retrieves the Verifiable Credential + Exchange ->> KyCH OAuth2 Gateway: POST /token\nContent-Type: application/x-www-form-urlencoded\ngrant_type=authorization_code\n&code={auth_code}\n&client_id={client_id}\n&client_secret={client_secret} + KyCH OAuth2 Gateway -->> Exchange: {access_token,\ntoken_type: "Bearer",\nexpires_in} + Exchange ->> KyCH OAuth2 Gateway: GET /info\nAuthorization: Bearer $ACCESS_TOKEN + KyCH OAuth2 Gateway -->> Exchange: $VERIFIABLE_CREDENTIAL Exchange -->> TalerWallet: Notify success TalerWallet ->> Exchange: Retry KYC-requiring original operation diff --git a/documentation/sequence_diagrams/token_sequence.txt b/documentation/sequence_diagrams/token_sequence.txt @@ -1,45 +1,45 @@ sequenceDiagram participant Client - participant Kych oauth2 Gateway - participant Kych oauth2 Gateway DB + participant KyCH OAuth2 Gateway + participant KyCH OAuth2 Gateway DB - Client ->> Kych oauth2 Gateway: POST /token\nContent-Type: application/x-www-form-urlencoded\ngrant_type=authorization_code&\ncode={code}&\nclient_id={client_id}&\nclient_secret={client_secret}&\nredirect_uri={redirect_uri} + Client ->> KyCH OAuth2 Gateway: POST /token\nContent-Type: application/x-www-form-urlencoded\ngrant_type=authorization_code&\ncode={code}&\nclient_id={client_id}&\nclient_secret={client_secret}&\nredirect_uri={redirect_uri} - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Validate grant_type ==\n'authorization_code' + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Validate grant_type ==\n'authorization_code' alt Invalid grant type - Kych oauth2 Gateway -->> Client: 400 BAD REQUEST\n{error: 'unsupported_grant_type'} + KyCH OAuth2 Gateway -->> Client: 400 BAD REQUEST\n{error: 'unsupported_grant_type'} else Valid grant type - Kych oauth2 Gateway ->> Kych oauth2 Gateway DB: SELECT id, secret_hash\nFROM clients WHERE client_id = $1 + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway DB: SELECT id, secret_hash\nFROM clients WHERE client_id = $1 alt Client not found or invalid secret - Kych oauth2 Gateway -->> Client: 401 UNAUTHORIZED\n{error: 'invalid_client'} + KyCH OAuth2 Gateway -->> Client: 401 UNAUTHORIZED\n{error: 'invalid_client'} else Client authenticated - Kych oauth2 Gateway ->> Kych oauth2 Gateway DB: WITH code_data AS (\n SELECT id, used AS was_already_used, session_id\n FROM authorization_codes\n WHERE code = $1 AND expires_at > NOW()\n FOR UPDATE\n),\nupdated_code AS (\n UPDATE authorization_codes ac\n SET used = TRUE,\n used_at = CASE WHEN NOT ac.used THEN NOW() ELSE ac.used_at END\n FROM code_data cd\n WHERE ac.id = cd.id\n RETURNING ac.id, ac.session_id\n)\nSELECT uc.id AS code_id,\n cd.was_already_used,\n uc.session_id,\n vs.status AS session_status,\n vs.client_id,\n vs.redirect_uri,\n at.token AS existing_token,\n at.expires_at AS token_expires_at\nFROM updated_code uc\nJOIN code_data cd ON uc.id = cd.id\nJOIN verification_sessions vs ON vs.id = uc.session_id\nLEFT JOIN access_tokens at\n ON at.session_id = vs.id AND at.revoked = FALSE + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway DB: WITH code_data AS (\n SELECT id, used AS was_already_used, session_id\n FROM authorization_codes\n WHERE code = $1 AND expires_at > NOW()\n FOR UPDATE\n),\nupdated_code AS (\n UPDATE authorization_codes ac\n SET used = TRUE,\n used_at = CASE WHEN NOT ac.used THEN NOW() ELSE ac.used_at END\n FROM code_data cd\n WHERE ac.id = cd.id\n RETURNING ac.id, ac.session_id\n)\nSELECT uc.id AS code_id,\n cd.was_already_used,\n uc.session_id,\n vs.status AS session_status,\n vs.client_id,\n vs.redirect_uri,\n at.token AS existing_token,\n at.expires_at AS token_expires_at\nFROM updated_code uc\nJOIN code_data cd ON uc.id = cd.id\nJOIN verification_sessions vs ON vs.id = uc.session_id\nLEFT JOIN access_tokens at\n ON at.session_id = vs.id AND at.revoked = FALSE alt No code found or DB error - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: 0 rows / Error - Kych oauth2 Gateway -->> Client: Error Response:\n- 400 BAD REQUEST {error: 'invalid_grant'}\n- 500 INTERNAL SERVER ERROR + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: 0 rows / Error + KyCH OAuth2 Gateway -->> Client: Error Response:\n- 400 BAD REQUEST {error: 'invalid_grant'}\n- 500 INTERNAL SERVER ERROR else Code found - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: code, session, and token data + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: code, session, and token data - Kych oauth2 Gateway ->> Kych oauth2 Gateway: Validate:\n- code belongs to client\n- redirect_uri matches stored value\n- code not already used\n- session status == 'verified' + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: Validate:\n- code belongs to client\n- redirect_uri matches stored value\n- code not already used\n- session status == 'verified' alt Token already exists (idempotent) - Kych oauth2 Gateway -->> Client: 200 OK\n{access_token: existing_token,\ntoken_type: 'Bearer', expires_in: 3600} + KyCH OAuth2 Gateway -->> Client: 200 OK\n{access_token: existing_token,\ntoken_type: 'Bearer', expires_in: 3600} else Invalid state - Kych oauth2 Gateway -->> Client: 400 BAD REQUEST\n{error: 'invalid_grant'}\n- Code belongs to different client\n- redirect_uri mismatch\n- Code already used\n- Session not verified + KyCH OAuth2 Gateway -->> Client: 400 BAD REQUEST\n{error: 'invalid_grant'}\n- Code belongs to different client\n- redirect_uri mismatch\n- Code already used\n- Session not verified else Valid - create token - Kych oauth2 Gateway ->> Kych oauth2 Gateway: generate_access_token() + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway: generate_access_token() - Kych oauth2 Gateway ->> Kych oauth2 Gateway DB: WITH updated AS (\n UPDATE verification_sessions\n SET status = 'completed', completed_at = NOW()\n WHERE id = $1 RETURNING id\n)\nINSERT INTO access_tokens\n(session_id, token, expires_at)\nVALUES ($1, $2, NOW() + INTERVAL '1 hour')\nRETURNING token, expires_at + KyCH OAuth2 Gateway ->> KyCH OAuth2 Gateway DB: WITH updated AS (\n UPDATE verification_sessions\n SET status = 'completed', completed_at = NOW()\n WHERE id = $1 RETURNING id\n)\nINSERT INTO access_tokens\n(session_id, token, expires_at)\nVALUES ($1, $2, NOW() + INTERVAL '1 hour')\nRETURNING token, expires_at alt Error - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: Error - Kych oauth2 Gateway -->> Client: 500 INTERNAL SERVER ERROR + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: Error + KyCH OAuth2 Gateway -->> Client: 500 INTERNAL SERVER ERROR else Success - Kych oauth2 Gateway DB -->> Kych oauth2 Gateway: token, expires_at - Kych oauth2 Gateway -->> Client: 200 OK\n{access_token, token_type: 'Bearer', expires_in: 3600} + KyCH OAuth2 Gateway DB -->> KyCH OAuth2 Gateway: token, expires_at + KyCH OAuth2 Gateway -->> Client: 200 OK\n{access_token, token_type: 'Bearer', expires_in: 3600} end end end diff --git a/documentation/taler-docs/README b/documentation/taler-docs/README @@ -1,15 +1,15 @@ -Kych Documentation for GNU Taler Docs +KyCH Documentation for GNU Taler Docs ====================================== -This directory contains reStructuredText documentation files for Kych +This directory contains reStructuredText documentation files for KyCH that are intended to be integrated into the GNU Taler documentation (https://git.taler.net/docs.git). Files ----- - taler-kych-manual.rst - Kych Operator Manual - core/api-kych.rst - Kych OAuth2 Gateway REST API specification + taler-kych-manual.rst - KyCH Operator Manual + core/api-kych.rst - KyCH OAuth2 Gateway REST API specification manpages/kych-client-management.1.rst - Man page for kych-client-management manpages/kych-oauth2-gateway.1.rst - Man page for kych-oauth2-gateway manpages/kych.conf.5.rst - Man page for kych.conf configuration @@ -22,11 +22,11 @@ Website Locations After building, the files will be available at: taler-kych-manual.rst - -> For Exchange Operators > 4. Kych Operator Manual + -> For Exchange Operators > 4. KyCH Operator Manual -> URL: /taler-kych-manual.html core/api-kych.rst - -> For Developers > API Specification > 17.9. Kych OAuth2 Gateway RESTful API + -> For Developers > API Specification > 17.9. KyCH OAuth2 Gateway RESTful API -> URL: /core/api-kych.html manpages/kych-client-management.1.rst @@ -63,7 +63,7 @@ Building the Documentation apt install graphviz python3-sphinx python3-recommonmark python3-sphinx-book-theme -4. Copy or symlink the Kych documentation files into taler-docs: +4. Copy or symlink the KyCH documentation files into taler-docs: # From the taler-docs directory: cp -r /path/to/kych/documentation/taler-docs/* . diff --git a/documentation/taler-docs/core/api-kych.rst b/documentation/taler-docs/core/api-kych.rst @@ -16,17 +16,17 @@ .. _kych-api: =============================== -Kych OAuth2 Gateway RESTful API +KyCH OAuth2 Gateway RESTful API =============================== -The Kych OAuth2 Gateway provides OAuth 2.0 authorization using OID4VP +The KyCH OAuth2 Gateway provides OAuth 2.0 authorization using OID4VP (OpenID for Verifiable Presentations) with the Swiyu Verifier. It allows OAuth 2.0 clients (such as a Taler exchange) to verify user identity via SD-JWT Verifiable Credentials stored in the user's Swiyu Wallet. The high-level flow is: -1. An OAuth 2.0 client is registered with Kych (via configuration file or CLI tool). +1. An OAuth 2.0 client is registered with KyCH (via configuration file or CLI tool). 2. When the client needs to verify a user's identity, it calls ``/setup/$CLIENT_ID`` with its client secret to obtain a ``nonce``. @@ -34,14 +34,14 @@ The high-level flow is: 3. The client redirects the user-agent to ``/authorize/$NONCE`` with OAuth 2.0 parameters (``response_type``, ``client_id``, ``redirect_uri``, ``state``, ``scope``). -4. Kych initiates a verification request with the Swiyu Verifier and returns +4. KyCH initiates a verification request with the Swiyu Verifier and returns a ``verification_url`` (QR code URL) for the user to scan with their Swiyu Wallet. 5. The user scans the QR code and presents their verifiable credential to the Swiyu Verifier. 6. Upon successful verification, Swiyu Verifier sends a webhook notification to - Kych's ``/notification`` endpoint. + KyCH's ``/notification`` endpoint. 7. The client polls ``/status/$VERIFICATION_ID`` or the user calls ``/finalize/$VERIFICATION_ID`` to be redirected back to the client with an @@ -63,7 +63,7 @@ Receiving Configuration .. http:get:: /config - Returns the public configuration of the Kych service, including supported + Returns the public configuration of the KyCH service, including supported verifiable credential types and available claims. Clients can use this endpoint to discover which claims they can request during authorization. @@ -73,16 +73,16 @@ Receiving Configuration **Response:** :http:statuscode:`200 OK`: - The service is operational. Returns a `KychConfigResponse` containing + The service is operational. Returns a `KyCHConfigResponse` containing the service configuration. - .. ts:def:: KychConfigResponse + .. ts:def:: KyCHConfigResponse - interface KychConfigResponse { - // Service identifier. Always "kych-oauth2-gateway". + interface KyCHConfigResponse { + // Service identifier. Always "KyCH OAuth2 Gateway". name: "kych-oauth2-gateway"; - // Semantic version of the Kych service (e.g., "1.0.0"). + // Semantic version of the KyCH service (e.g., "1.0.0"). version: string; // Health status indicator. "healthy" indicates the service is @@ -90,7 +90,7 @@ Receiving Configuration status: "healthy"; // Verifiable credential type identifier configured for this - // Kych instance. For Swiss Beta ID, this is "betaid-sdjwt". + // KyCH instance. For Swiss Beta ID, this is "betaid-sdjwt". vc_type: string; // Credential format. SD-JWT credentials use "vc+sd-jwt". @@ -121,7 +121,7 @@ Setup endpoint. The client authenticates using HTTP Bearer authentication with its client - secret. Upon success, Kych creates a new session in ``pending`` status and + secret. Upon success, KyCH creates a new session in ``pending`` status and returns a cryptographically random nonce that identifies this session. **Request:** @@ -134,7 +134,7 @@ Setup **Response:** :http:statuscode:`200 OK`: - Session created successfully. Returns a `KychSetupResponse` containing + Session created successfully. Returns a `KyCHSetupResponse` containing the session nonce. :http:statuscode:`401 Unauthorized`: @@ -146,9 +146,9 @@ Setup The ``$CLIENT_ID`` in the URL path does not match any registered client. The client must be registered via configuration file or CLI before use. - .. ts:def:: KychSetupResponse + .. ts:def:: KyCHSetupResponse - interface KychSetupResponse { + interface KyCHSetupResponse { // Cryptographically random nonce (base64-encoded) that identifies // this verification session. The client must include this nonce // in the URL when redirecting the user to /authorize/$NONCE. @@ -168,7 +168,7 @@ Authorize The OAuth 2.0 authorization endpoint. The client redirects the user's browser to this endpoint to begin the credential verification process. - When called, Kych performs the following steps: + When called, KyCH performs the following steps: 1. Validates the session identified by ``$NONCE`` exists and is in ``pending`` status. 2. Validates the OAuth 2.0 parameters (response_type, client_id, redirect_uri, scope). @@ -194,10 +194,10 @@ Authorize :query redirect_uri: Required. The URI where the user will be redirected after verification completes. Must exactly match the redirect URI registered for - this client in Kych's configuration. Mismatches are rejected for security. + this client in KyCH's configuration. Mismatches are rejected for security. :query state: Required. An opaque value generated by the client for CSRF - protection. Kych stores this value and includes it in all subsequent + protection. KyCH stores this value and includes it in all subsequent responses and redirects. The client must verify the state matches when receiving the callback to prevent cross-site request forgery attacks. @@ -211,7 +211,7 @@ Authorize :http:statuscode:`200 OK`: Verification request created successfully. If the ``Accept`` header includes ``text/html``, returns an HTML page containing a QR code and JavaScript that - polls the status endpoint. Otherwise, returns a `KychAuthorizeResponse` JSON + polls the status endpoint. Otherwise, returns a `KyCHAuthorizeResponse` JSON object containing the verification URL. :http:statuscode:`400 Bad Request`: @@ -236,13 +236,13 @@ Authorize the server. The client should call ``/setup`` to create a new session. :http:statuscode:`502 Bad Gateway`: - Kych failed to communicate with the Swiyu Verifier when creating the + KyCH failed to communicate with the Swiyu Verifier when creating the verification request. This may indicate network issues or verifier unavailability. The client may retry after a delay. - .. ts:def:: KychAuthorizeResponse + .. ts:def:: KyCHAuthorizeResponse - interface KychAuthorizeResponse { + interface KyCHAuthorizeResponse { // UUID assigned by the Swiyu Verifier to identify this verification // request. Used in the /status and /finalize endpoint URLs. verificationId: string; @@ -292,7 +292,7 @@ Status **Response:** :http:statuscode:`200 OK`: - Returns a `KychStatusResponse` containing the current verification status. + Returns a `KyCHStatusResponse` containing the current verification status. :http:statuscode:`403 Forbidden`: The ``state`` parameter does not match the state associated with this @@ -303,9 +303,9 @@ Status No verification exists with the given ``$VERIFICATION_ID``. The ID may be invalid or the verification may have been deleted after expiration. - .. ts:def:: KychStatusResponse + .. ts:def:: KyCHStatusResponse - interface KychStatusResponse { + interface KyCHStatusResponse { // Current status of the verification. Possible values: // // - "pending": Session created but /authorize not yet called. @@ -346,7 +346,7 @@ Finalize with an OAuth 2.0 authorization code. This endpoint should only be called after polling ``/status`` returns ``verified``. - When called, Kych: + When called, KyCH: 1. Validates the session is in ``verified`` status. 2. Generates a cryptographically random authorization code. @@ -413,9 +413,9 @@ Token The request body contains the following form parameters: - .. ts:def:: KychTokenRequest + .. ts:def:: KyCHTokenRequest - interface KychTokenRequest { + interface KyCHTokenRequest { // The OAuth 2.0 grant type. Must be "authorization_code". grant_type: "authorization_code"; @@ -427,7 +427,7 @@ Token // the authorization flow. client_id: string; - // The client secret for authentication. Kych verifies this + // The client secret for authentication. KyCH verifies this // against the stored bcrypt hash. client_secret: string; @@ -440,7 +440,7 @@ Token **Response:** :http:statuscode:`200 OK`: - Token issued successfully. Returns a `KychTokenResponse` containing + Token issued successfully. Returns a `KyCHTokenResponse` containing the access token. The session status is updated to ``completed``. :http:statuscode:`400 Bad Request`: @@ -455,9 +455,9 @@ Token Client authentication failed. Either ``client_id`` does not exist or ``client_secret`` does not match the registered secret. - .. ts:def:: KychTokenResponse + .. ts:def:: KyCHTokenResponse - interface KychTokenResponse { + interface KyCHTokenResponse { // The access token. Use this as a Bearer token in the // Authorization header when calling /info. access_token: string; @@ -499,15 +499,15 @@ Info **Response:** :http:statuscode:`200 OK`: - Returns a `KychInfoResponse` containing the verified claims. + Returns a `KyCHInfoResponse` containing the verified claims. :http:statuscode:`401 Unauthorized`: The access token is missing, malformed, expired, or has been revoked. The client should obtain a new token by starting a new verification flow. - .. ts:def:: KychInfoResponse + .. ts:def:: KyCHInfoResponse - interface KychInfoResponse { + interface KyCHInfoResponse { // The verified credential claims as key-value pairs. Only claims // that were requested and disclosed are included. // @@ -539,7 +539,7 @@ Notification Verifier. The verifier calls this endpoint when a verification request changes state, typically when the user presents their credential. - When a notification is received, Kych: + When a notification is received, KyCH: 1. Looks up the verification session by ``verification_id``. 2. Queries the Swiyu Verifier's management API for the verification result. @@ -555,11 +555,11 @@ Notification :reqheader Content-Type: Should be ``application/json``. - .. ts:def:: KychNotificationRequest + .. ts:def:: KyCHNotificationRequest - interface KychNotificationRequest { + interface KyCHNotificationRequest { // UUID of the verification request as assigned by the Swiyu - // Verifier. Kych uses this to look up the corresponding session. + // Verifier. KyCH uses this to look up the corresponding session. verification_id: string; // ISO 8601 timestamp indicating when the verification state @@ -582,9 +582,9 @@ Errors All error responses use the following format: -.. ts:def:: KychErrorResponse +.. ts:def:: KyCHErrorResponse - interface KychErrorResponse { + interface KyCHErrorResponse { // Error code identifier error: string; } diff --git a/documentation/taler-docs/kych.conf.5.rst b/documentation/taler-docs/kych.conf.5.rst @@ -6,13 +6,13 @@ kych.conf(5) Name ==== - **kych.conf** - Kych OAuth2 Gateway configuration file + **kych.conf** - KyCH OAuth2 Gateway configuration file Description =========== -The Kych OAuth2 Gateway uses an INI-style configuration file. The configuration +The KyCH OAuth2 Gateway uses an INI-style configuration file. The configuration defines server binding options, database connection, cryptographic parameters, verifiable credential settings, and OAuth2 client configurations. @@ -81,7 +81,7 @@ AUTH_CODE_TTL_MINUTES Verifiable Credential Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -These options define the verifiable credential type that Kych will request +These options define the verifiable credential type that KyCH will request from the Swiyu Verifier. VC_TYPE diff --git a/documentation/taler-docs/manpages/kych-client-management.1.rst b/documentation/taler-docs/manpages/kych-client-management.1.rst @@ -4,7 +4,7 @@ kych-client-management(1) Name ==== -**kych-client-management** - Manage OAuth2 Gateway clients for Kych +**kych-client-management** - Manage OAuth2 Gateway clients for KyCH Synopsis ======== @@ -15,7 +15,7 @@ Description =========== **kych-client-management** is a command-line tool for managing OAuth2 clients -in the Kych OAuth2 Gateway. It allows administrators to create, update, delete, +in the KyCH OAuth2 Gateway. It allows administrators to create, update, delete, list, and synchronize client configurations stored in the database. Global Options diff --git a/documentation/taler-docs/taler-kych-manual.rst b/documentation/taler-docs/taler-kych-manual.rst @@ -15,19 +15,19 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -Kych Operator Manual +KyCH Operator Manual #################### Introduction ============ -About Kych +About KyCH ---------- -Kych is an OAuth 2.0-compatible gateway for KYC (Know Your Customer) verification +KyCH is an OAuth 2.0-compatible gateway for KYC (Know Your Customer) verification using OID4VP (OpenID for Verifiable Presentations) with Swiyu Verifier. -By redirecting a user-agent to a Kych service, a client (such as a Taler exchange) -can have Kych verify the user's identity through verifiable credentials stored +By redirecting a user-agent to a KyCH service, a client (such as a Taler exchange) +can have KyCH verify the user's identity through verifiable credentials stored in the user's Swiyu Wallet and obtain the verified identity attributes via the ``/info`` endpoint. @@ -36,7 +36,7 @@ About this manual ----------------- This manual targets system administrators who want to install, -operate or integrate a Kych service. To report issues +operate or integrate a KyCH service. To report issues or learn about known limitations, please check our `bug tracker <https://bugs.taler.net>`__. @@ -44,26 +44,20 @@ or learn about known limitations, please check our Architecture overview --------------------- -The following diagram shows the high-level architecture of Kych and its +The following diagram shows the high-level architecture of KyCH and its interactions with other components: .. image:: images/kych_overview.jpg - :alt: Kych Architecture Overview + :alt: KyCH Architecture Overview :align: center -Kych acts as an OAuth 2.0 gateway that orchestrates KYC verification between: +KyCH acts as an OAuth 2.0 gateway that orchestrates KYC verification between: - **Taler Exchange**: The client that requires KYC verification for users. -- **Kych OAuth2 Gateway**: Manages OAuth 2.0 flows and verification sessions. +- **KyCH OAuth2 Gateway**: Manages OAuth 2.0 flows and verification sessions. - **Swiyu Verifier**: Handles OID4VP verification requests. - **Swiyu Wallet**: The user's mobile wallet containing verifiable credentials. -The following sequence diagram illustrates the complete verification flow: - -.. image:: images/swiyu_taler_sequence_diagram.png - :alt: Swiyu-Taler Verification Sequence Diagram - :align: center - The flow proceeds as follows: 1. The *resource owner* (user) initiates a KYC-requiring operation with the @@ -76,7 +70,7 @@ The flow proceeds as follows: OAuth 2.0 parameters (``response_type``, ``client_id``, ``redirect_uri``, ``state``, ``scope``). -4. Kych creates a verification request with the Swiyu Verifier and returns +4. KyCH creates a verification request with the Swiyu Verifier and returns an HTML page with a QR code (or JSON with ``verification_url``). 5. The user scans the QR code with their Swiyu Wallet, which retrieves the @@ -85,13 +79,13 @@ The flow proceeds as follows: 6. Upon user consent, the Swiyu Wallet presents the requested credentials to the Swiyu Verifier. -7. The Swiyu Verifier sends a webhook notification to Kych's ``/notification`` +7. The Swiyu Verifier sends a webhook notification to KyCH's ``/notification`` endpoint with the verification result. 8. The client polls ``GET /status/{verification_id}`` until the status becomes ``verified``, then redirects to ``GET /finalize/{verification_id}``. -9. Kych redirects the user to the client's ``redirect_uri`` with an +9. KyCH redirects the user to the client's ``redirect_uri`` with an authorization code. 10. The client exchanges the authorization code for an access token via @@ -101,7 +95,7 @@ The flow proceeds as follows: the access token. -.. _KychInstallation: +.. _KyCHInstallation: Installation ============ @@ -109,7 +103,7 @@ Installation Prerequisites ------------- -Before installing Kych, ensure you have the following: +Before installing KyCH, ensure you have the following: * Rust toolchain (stable, version 1.70 or later) * PostgreSQL 12 or later @@ -119,7 +113,7 @@ Before installing Kych, ensure you have the following: Building from source -------------------- -Clone the Kych repository and build the release binaries: +Clone the KyCH repository and build the release binaries: .. code-block:: shell-session @@ -136,11 +130,11 @@ The compiled binaries will be available in ``target/release/``: Database setup -------------- -Kych uses PostgreSQL to store client configurations, verification sessions, +KyCH uses PostgreSQL to store client configurations, verification sessions, authorization codes, and access tokens. First, switch to the ``postgres`` user and create a database user and database -for Kych: +for KyCH: .. code-block:: shell-session @@ -153,7 +147,7 @@ The ``createuser`` command creates a PostgreSQL role named ``kych``. The ``createdb`` command creates a database named ``kych`` owned by the ``kych`` role (``-O kych``). -Next, initialize the database schema. Kych uses a versioning system to manage +Next, initialize the database schema. KyCH uses a versioning system to manage database migrations. First, install the versioning support, then apply the schema migration: @@ -174,7 +168,7 @@ schema with the following tables: .. note:: The SQL migration files are located in the ``oauth2_gatewaydb/`` directory - of the Kych source tree. Adjust the path if you installed Kych to a + of the KyCH source tree. Adjust the path if you installed KyCH to a different location. @@ -184,7 +178,7 @@ Configuration Configuration file location --------------------------- -Kych reads its configuration from a file in INI format. The default location +KyCH reads its configuration from a file in INI format. The default location is ``/etc/kych/kych.conf``. You can specify an alternative path using the ``--config`` or ``-c`` command-line option. @@ -224,13 +218,13 @@ The main configuration is specified in the ``[kych-oauth2-gateway]`` section: Server binding options ---------------------- -Kych can listen on either a TCP socket or a Unix domain socket, but not both +KyCH can listen on either a TCP socket or a Unix domain socket, but not both simultaneously. For production deployments behind a reverse proxy (recommended), Unix sockets provide better security by avoiding network exposure. **TCP binding:** -Use TCP binding for development or when Kych must be accessible over the network. +Use TCP binding for development or when KyCH must be accessible over the network. - ``HOST``: The IP address to bind to. Use ``127.0.0.1`` for localhost-only access or ``0.0.0.0`` to accept connections on all interfaces. @@ -239,10 +233,10 @@ Use TCP binding for development or when Kych must be accessible over the network **Unix socket binding:** Use Unix sockets for production deployments behind nginx or another reverse proxy. -This avoids exposing Kych directly to the network. +This avoids exposing KyCH directly to the network. - ``UNIXPATH``: Path to the Unix domain socket file (e.g., ``/run/kych/kych.sock``). - Ensure the directory exists and is writable by the Kych process. + Ensure the directory exists and is writable by the KyCH process. - ``UNIXPATH_MODE``: Octal permission mode for the socket file (default: ``666``). Set to ``660`` if only the reverse proxy user needs access. @@ -250,7 +244,7 @@ This avoids exposing Kych directly to the network. Database configuration ---------------------- -Kych requires a PostgreSQL database to persist client registrations, verification +KyCH requires a PostgreSQL database to persist client registrations, verification sessions, authorization codes, and access tokens. The connection is specified using a standard PostgreSQL connection URI. @@ -266,7 +260,7 @@ manager to avoid storing database credentials in plain text configuration files. Cryptographic parameters ------------------------ -Kych generates cryptographically secure random values for nonces, access tokens, +KyCH generates cryptographically secure random values for nonces, access tokens, and authorization codes. These parameters control the size (entropy) and lifetime of these values. Larger sizes provide more security but result in longer token strings. @@ -292,7 +286,7 @@ longer token strings. Verifiable Credential settings ------------------------------ -These settings define the type of verifiable credential that Kych will request +These settings define the type of verifiable credential that KyCH will request from the Swiyu Verifier during the OID4VP flow. The configuration must match the credential type supported by your Swiyu Verifier deployment and the credentials available in users' Swiyu Wallets. @@ -392,7 +386,7 @@ details: Client Management ================= -OAuth 2.0 clients (such as Taler exchanges) must be registered with Kych before +OAuth 2.0 clients (such as Taler exchanges) must be registered with KyCH before they can initiate KYC verification flows. Each client has its own credentials, redirect URI, and may use a different Swiyu Verifier instance. @@ -427,14 +421,14 @@ Each section name must start with ``client_`` followed by a unique identifier **Client configuration options:** - ``CLIENT_ID``: Unique identifier that the client uses to authenticate with - Kych. This is passed in the ``client_id`` parameter during OAuth 2.0 flows + KyCH. This is passed in the ``client_id`` parameter during OAuth 2.0 flows and in the ``/setup/{client_id}`` endpoint URL. - ``CLIENT_SECRET``: Shared secret for client authentication. The client provides this as a Bearer token in the ``Authorization`` header when calling ``/setup``, and in the request body when calling ``/token``. Use the ``secret-token:`` prefix per RFC 8959 for secrets that should not be logged. - Kych stores the secret as a bcrypt hash in the database. + KyCH stores the secret as a bcrypt hash in the database. - ``VERIFIER_URL``: Base URL of the Swiyu Verifier instance that this client will use for credential verification. Different clients can use different @@ -448,7 +442,7 @@ Each section name must start with ``client_`` followed by a unique identifier - ``REDIRECT_URI``: The OAuth 2.0 redirect URI where users are sent after completing verification. This must exactly match the ``redirect_uri`` parameter provided by the client in authorization requests. For security, - Kych rejects requests with mismatched redirect URIs. + KyCH rejects requests with mismatched redirect URIs. - ``ACCEPTED_ISSUER_DIDS``: List of trusted credential issuer DIDs (Decentralized Identifiers) in bracketed format. Only credentials issued by these DIDs will @@ -563,13 +557,13 @@ Deployment systemd service --------------- -Create a systemd service file for Kych: +Create a systemd service file for KyCH: .. code-block:: ini :caption: /etc/systemd/system/kych.service [Unit] - Description=Kych OAuth2 Gateway + Description=KyCH OAuth2 Gateway After=network.target postgresql.service [Service] @@ -595,7 +589,7 @@ Enable and start the service: Reverse proxy setup ------------------- -Kych should be deployed behind a reverse proxy that provides TLS termination, +KyCH should be deployed behind a reverse proxy that provides TLS termination, as required by OAuth 2.0. The following example shows an nginx configuration using a Unix socket: @@ -634,7 +628,7 @@ Enable the site: Webhook configuration --------------------- -Kych receives callbacks from the Swiyu Verifier when a verification request +KyCH receives callbacks from the Swiyu Verifier when a verification request changes state (e.g., when the user presents their credential). The webhook endpoint is ``/notification``. @@ -648,7 +642,7 @@ This endpoint must be accessible from the Swiyu Verifier service. .. note:: - The Swiyu Verifier must be configured to send notifications to the Kych + The Swiyu Verifier must be configured to send notifications to the KyCH gateway. Consult the Swiyu Verifier documentation for details on configuring webhook callbacks. The verifier will POST a JSON payload containing the ``verification_id`` and ``timestamp`` when a verification completes. @@ -657,7 +651,7 @@ This endpoint must be accessible from the Swiyu Verifier service. Integration with Taler Exchange =============================== -To use Kych as a KYC provider for a GNU Taler exchange, configure the +To use KyCH as a KYC provider for a GNU Taler exchange, configure the exchange with the following settings: .. code-block:: ini @@ -673,14 +667,14 @@ exchange with the following settings: KYC_OAUTH2_CLIENT_SECRET = secret-token:your-secure-secret KYC_OAUTH2_POST_URL = https://kych.example.com -The exchange will then use Kych for KYC verification when the +The exchange will then use KyCH for KYC verification when the ``KYCH_IDENTITY`` check is required. OAuth 2.0 endpoints ------------------- -Kych provides the following OAuth 2.0 endpoints: +KyCH provides the following OAuth 2.0 endpoints: - ``/authorize`` - Authorization endpoint (redirects user to Swiyu Wallet flow) - ``/token`` - Token endpoint (exchanges authorization code for access token) diff --git a/documentation/taler-docs/taler-swiyu-verifier-manual.rst b/documentation/taler-docs/taler-swiyu-verifier-manual.rst @@ -0,0 +1,1216 @@ +.. + This file is part of GNU TALER. + + Copyright (C) 2024, 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + +Swiyu Verifier Operator Manual +############################## + +Introduction +============ + +About Swiyu Verifier +-------------------- + +The Swiyu Generic Verifier Service is a web server implementing the technical +standards specified in the Swiyu Trust Infrastructure Interoperability Profile. +It provides the capability to verify Verifiable Credentials held in users' +digital wallets using the OpenID for Verifiable Presentations (OID4VP) +protocol. + +The verifier service exposes two distinct API interfaces: + +- **Management API**: Internal interface for business systems to create + verification requests and retrieve results. This API should only be + accessible from within your organization's network. + +- **OID4VP API**: Public interface that implements the OpenID for Verifiable + Presentations protocol. This interface must be accessible by wallet + applications and requires HTTPS with a valid TLS certificate. + +When integrated with KyCH OAuth2 Gateway, the Swiyu Verifier enables +KYC (Know Your Customer) verification flows for GNU Taler exchanges, +allowing users to prove their identity attributes using Swiss Beta ID +credentials stored in the Swiyu Wallet. + + +About this manual +----------------- + +This manual targets system administrators who want to install, configure, +and operate a Swiyu Verifier service. It covers the onboarding process +to the Swiyu Trust Infrastructure, service configuration, integration +with KyCH OAuth2 Gateway, and API usage. + + +Architecture overview +--------------------- + +The following diagram illustrates the high-level architecture of credential +verification using the Swiyu Verifier: + +:: + + +------------------+ +------------------+ +------------------+ + | | | | | | + | Business |---->| Swiyu |<----| Swiyu | + | Verifier | | Verifier | | Wallet | + | (e.g., KyCH) | | Service | | | + | | | | | | + +------------------+ +------------------+ +------------------+ + | + v + +------------------+ + | | + | Swiyu | + | Registries | + | | + +------------------+ + +The verification flow proceeds as follows: + +1. The business verifier (such as KyCH) creates a verification request + via the Management API, specifying which credential claims to verify. + +2. The verifier service stores the request and returns a verification URI + and optional deeplink for QR code generation. + +3. The user's wallet retrieves the verification request object via the + OID4VP API and displays the requested claims for user consent. + +4. Upon user consent, the wallet sends the authorization response containing + the Verifiable Presentation with the requested credential data. + +5. The verifier service validates the credential by checking the issuer's + public key against the Base Registry and verifying the credential status + against the Status Registry. + +6. The business verifier retrieves the verification result and disclosed + claims either by polling or via webhook notification. + + +Prerequisites +============= + +System requirements +------------------- + +Before deploying the Swiyu Verifier, ensure your system meets these requirements: + +- **Java Runtime Environment (JRE)**: Version 21 or higher +- **PostgreSQL**: Version 12 or higher +- **Disk space**: Approximately 100 MB +- **Network access**: Outbound access to Swiyu registries and inbound access + for wallet connections +- **Operating system**: Linux x64/AArch64, macOS AArch64, or Windows x64 + + +HTTPS requirement +----------------- + +The Swiyu Wallet only accepts HTTPS connections with valid TLS certificates. +Your verifier's OID4VP endpoints must be accessible via HTTPS using a +certificate from a trusted Certificate Authority. + +For development and testing, you can use a tunneling service like ngrok +to expose your local verifier with a valid HTTPS URL: + +.. code-block:: shell-session + + $ ngrok http 8080 + +This provides a public HTTPS URL (e.g., ``https://abc123.ngrok-free.app``) +that forwards to your local verifier. Use this URL as the ``external-url`` +in your configuration. + +For production deployments, obtain a proper TLS certificate and configure +your reverse proxy or load balancer accordingly. + + +Onboarding to Swiyu Trust Infrastructure +======================================== + +Before operating a verifier in the Swiyu ecosystem, you must complete the +onboarding process to register your service on the Base Registry. This +section summarizes the required steps. + +The onboarding process involves several administrative and technical steps +that establish your identity as a trusted verifier within the Swiyu Trust +Infrastructure. + +.. note:: + + The official Swiyu documentation provides detailed cookbooks that guide + you through each step of the onboarding process: + + - `Onboarding the swiyu Base & Trust Registry <https://swiyu-admin-ch.github.io/cookbooks/onboarding-base-and-trust-registry/>`_: + Complete guide for registering your organization and managing DIDs + + - `Getting started with the swiyu Generic Verifier <https://swiyu-admin-ch.github.io/cookbooks/onboarding-generic-verifier/>`_: + Step-by-step deployment guide for the verifier service + + - `Designing the visualization for Issuer, Verifier and Credential <https://swiyu-admin-ch.github.io/cookbooks/vc-visual-presentation/>`_: + How to configure the display metadata shown in wallets + + - `How to use Beta-ID for my business <https://swiyu-admin-ch.github.io/cookbooks/how-to-use-beta-id/>`_: + Details about the Swiss Beta ID credential and available claims + + The following sections provide a summary. Refer to the official cookbooks + for the most up-to-date and detailed instructions. + + +Step 1: Access the Swiss Confederacy ePortal +-------------------------------------------- + +Sign in or create an account at the Swiss Confederacy ePortal using +an AGOV or CH-Login account. This account is required for all +subsequent registration steps. + +The ePortal is available at https://www.eportal.admin.ch/. + + +Step 2: Register as Business Partner +------------------------------------ + +Complete the business partner registration process through the ePortal. +This establishes your organization's identity within the trust infrastructure. + +Navigate to the Swiyu Public Beta section and follow the business partner +registration workflow. + + +Step 3: Obtain API Keys +----------------------- + +Access the API self-service portal to generate the API keys required for +interacting with the Swiyu registries. These keys authenticate your +verifier service when making registry requests. + +The self-service portal provides: + +- API credentials for the Base Registry (DID management) +- API credentials for the Status Registry (credential revocation checks) + + +Step 4: Allocate DID Space +-------------------------- + +Request allocation of a Decentralized Identifier (DID) space on the +Swiyu Base Registry. This provides a unique namespace for your verifier's +cryptographic identity. + +The DID follows the ``did:tdw`` method (Trust DID Web) with the format:: + + did:tdw:<hash>:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:<uuid> + + +Step 5: Generate Cryptographic Keys +----------------------------------- + +Use the Swiyu DID Toolbox to generate the required cryptographic keys +and DID log. The toolbox creates: + +- An EC P-256 private key for signing verification requests +- The corresponding DID Document entries +- A DID log file for registration + +Download the DID Toolbox from the official Swiyu repository and run: + +.. code-block:: shell-session + + $ java -jar didtoolbox.jar generate + +This creates a ``.didtoolbox`` directory containing your keys and DID log. +The key files include: + +- ``auth-key-01``: The private authentication key (keep this secure) +- ``did-log.json``: The DID log to upload to the registry + +The generated DID log contains the values needed for verifier configuration: + +- ``value.id``: Your verifier's DID (used for ``client_id``) +- ``value.assertionMethod``: The key reference (used for ``signing-key-verification-method``) + + +Step 6: Upload DID Log +---------------------- + +Upload the generated DID log to the Base Registry using the API keys +obtained in Step 3. This publishes your verifier's DID Document, +making your public key available for credential verification. + +Use the Base Registry API to upload your DID log:: + + POST https://identifier-reg.trust-infra.swiyu.admin.ch/api/v1/did/<your-uuid>/log + +After successful upload, your DID is resolvable and wallets can verify +your signed request objects. + + +Step 7: (Optional) Become a Trusted Participant +----------------------------------------------- + +Optionally register on the Trust Registry to become a trusted participant. +This enables other parties to verify your verifier's trustworthiness +through the trust infrastructure. + +See the `Onboarding the swiyu Base & Trust Registry cookbook +<https://swiyu-admin-ch.github.io/cookbooks/onboarding-base-and-trust-registry/>`_ +for details on trust registry registration. + + +Installation +============ + +Building from source +-------------------- + +Clone the Swiyu Verifier repository and build using Maven: + +.. code-block:: shell-session + + $ git clone https://github.com/swiyu-admin-ch/swiyu-verifier.git + $ cd swiyu-verifier + $ ./mvnw clean install -DskipTests + +This produces the executable JAR files in the ``verifier-application/target`` +directory. + + +Database setup +-------------- + +The Swiyu Verifier requires a PostgreSQL database to store verification +requests and results. Create the database and user with appropriate +privileges: + +.. code-block:: shell-session + + $ psql -U postgres + +.. code-block:: sql + + CREATE USER verifier_user WITH PASSWORD 'your_secure_password'; + CREATE DATABASE verifier_db OWNER verifier_user; + GRANT ALL PRIVILEGES ON DATABASE verifier_db TO verifier_user; + +The ``CREATE USER`` command creates a PostgreSQL role that the verifier +service will use to authenticate with the database. The ``CREATE DATABASE`` +command creates the database and assigns ownership to the verifier user. +The ``GRANT`` command ensures the user has full access to the database. + +The verifier service uses Hibernate to automatically create the required +schema on first startup when configured with ``ddl-auto: create`` or +``ddl-auto: update``. + + +Configuration +============= + +The Swiyu Verifier uses Spring Boot's externalized configuration system, +which supports YAML configuration files, environment variables, and +profile-based overrides. This section explains the configuration profiles +and how to use them for different deployment scenarios. + + +Configuration profiles +---------------------- + +Spring Boot profiles allow you to define environment-specific configurations +that override the base ``application.yml`` settings. The verifier ships with +several pre-configured profiles for different deployment scenarios. + +**Base configuration (application.yml)** + +The base configuration file defines all available settings with environment +variable placeholders. This configuration is designed for production +deployments where settings are injected via environment variables or +container orchestration: + +.. code-block:: yaml + + application: + external-url: "${EXTERNAL_URL:}" + client_id: "${VERIFIER_DID:}" + client_id_scheme: "did" + signing_key: "${secret.signing_key:${SIGNING_KEY:}}" + signing-key-verification-method: "${DID_VERIFICATION_METHOD:}" + client-metadata-file: "${OPENID_CLIENT_METADATA_FILE:}" + deeplink-schema: ${DEEPLINK_SCHEMA:swiyu-verify} + + spring: + datasource: + url: "${POSTGRES_JDBC}" + username: "${secret.db.username:${POSTGRES_USER}}" + password: "${secret.db.password:${POSTGRES_PASSWORD}}" + jpa: + hibernate: + ddl-auto: validate + +The ``${VARIABLE:default}`` syntax means the value is read from the +environment variable ``VARIABLE``, falling back to ``default`` if not set. +The nested form ``${secret.value:${ENV_VAR:}}`` first checks for a +mounted secret file, then an environment variable. + +**Local profile with Docker Compose (application-local.yml)** + +The ``local`` profile is designed for development with Docker Compose +managing the PostgreSQL database. When this profile is active, Spring Boot +automatically starts a PostgreSQL container: + +.. code-block:: yaml + + application: + signing_key: "-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----\n" + signing-key-verification-method: "did:example:12345#key-1" + external-url: "http://${server.host}:${server.port}" + client_id: "did:example:12345" + client-metadata-file: "classpath:client_metadata.json" + + spring: + docker: + compose: + enabled: true + file: compose.yaml + datasource: + url: "jdbc:postgresql://localhost:5434/verifier_db" + username: "verifier_user" + password: "secret" + jpa: + hibernate: + ddl-auto: create + + logging: + level: + ch.admin.bj.swiyu: DEBUG + +Key characteristics: + +- ``spring.docker.compose.enabled: true`` enables Spring Boot's Docker + Compose support, which starts containers defined in ``compose.yaml`` +- ``ddl-auto: create`` generates the database schema automatically +- Uses example DID values for testing (not valid for real verifications) +- Debug logging enabled for development visibility +- Database runs on port 5434 (mapped from container's 5432) + +The accompanying ``compose.yaml`` defines the PostgreSQL container: + +.. code-block:: yaml + + services: + postgres: + image: postgres:15-alpine + environment: + POSTGRES_USER: "verifier_user" + POSTGRES_PASSWORD: "secret" + POSTGRES_DB: "verifier_db" + ports: + - '5434:5432' + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + +**Local dockerless profile (application-local-dockerless.yml)** + +The ``local-dockerless`` profile runs without Docker, connecting directly +to a PostgreSQL instance on your machine. This is useful when you have +PostgreSQL installed locally or want more control over the database: + +.. code-block:: yaml + + application: + external-url: "https://your-ngrok-url.ngrok-free.app" + client_id: "did:tdw:QmHash:identifier-reg.trust-infra.swiyu-int.admin.ch:api:v1:did:your-uuid" + client_id_scheme: "did" + signing_key: | + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEI...your_actual_key... + -----END EC PRIVATE KEY----- + signing-key-verification-method: "did:tdw:QmHash:identifier-reg.trust-infra.swiyu-int.admin.ch:api:v1:did:your-uuid#auth-key-01" + client-metadata-file: "classpath:/client_metadata.json" + + spring: + docker: + compose: + enabled: false + datasource: + url: "jdbc:postgresql://localhost:5432/verifier_db" + username: "verifier_user" + password: "secret" + jpa: + hibernate: + ddl-auto: create + + logging: + level: + ch.admin.bj.swiyu: DEBUG + + webhook: + callback-uri: "https://kych.example.com/notification" + callback-interval: 5000 + +Key characteristics: + +- ``spring.docker.compose.enabled: false`` disables Docker Compose support +- Uses real DID values from the Swiyu Trust Infrastructure onboarding +- Requires manual PostgreSQL setup (see Database Setup section) +- Webhook configuration for integration testing with KyCH +- Uses ngrok or similar for HTTPS exposure to wallets + + +Environment variables file (.env) +--------------------------------- + +For convenience, you can define environment variables in a ``.env`` file +in the resources directory. Spring Boot loads these automatically: + +.. code-block:: bash + + # Verifier identity + EXTERNAL_URL="https://your-verifier.ngrok-free.app" + VERIFIER_DID="did:tdw:QmHash:identifier-reg.trust-infra.swiyu-int.admin.ch:api:v1:did:your-uuid" + DID_VERIFICATION_METHOD="did:tdw:QmHash:identifier-reg.trust-infra.swiyu-int.admin.ch:api:v1:did:your-uuid#auth-key-01" + OPENID_CLIENT_METADATA_FILE="classpath:/client_metadata.json" + + SIGNING_KEY="-----BEGIN EC PRIVATE KEY----- + MHcCAQEEI...your_key_content... + -----END EC PRIVATE KEY-----" + + # Database connection + POSTGRES_USER="verifier_user" + POSTGRES_PASSWORD="secret" + POSTGRES_JDBC="jdbc:postgresql://localhost:5432/verifier_db" + +This approach separates sensitive values from the configuration files and +works well with the base ``application.yml`` profile. + + +Deployment Options +================== + +The Swiyu Verifier supports multiple deployment strategies depending on +your infrastructure and requirements. + + +Local development without Docker +-------------------------------- + +This approach runs the verifier directly on your machine with a locally +installed PostgreSQL database. + +**1. Set up PostgreSQL** + +Create the database and user: + +.. code-block:: shell-session + + $ psql -d postgres -c "CREATE USER verifier_user WITH PASSWORD 'secret';" + $ psql -d postgres -c "CREATE DATABASE verifier_db OWNER verifier_user;" + $ psql -d verifier_db -c "GRANT ALL PRIVILEGES ON DATABASE verifier_db TO verifier_user;" + +**2. Configure the profile** + +Create or edit ``application-local-dockerless.yml`` with your DID credentials +obtained from the Swiyu onboarding process. + +**3. Start the verifier** + +.. code-block:: shell-session + + $ ./mvnw spring-boot:run -pl verifier-application \ + -Dspring-boot.run.profiles=local-dockerless + +**4. Expose via ngrok (for wallet testing)** + +Since wallets require HTTPS, use ngrok to expose your local verifier: + +.. code-block:: shell-session + + $ ngrok http 8080 + +Update the ``external-url`` in your configuration with the ngrok URL. + + +Local development with Docker Compose +------------------------------------- + +This approach uses Docker Compose to manage the PostgreSQL database while +running the verifier application directly. + +**1. Start the verifier with Docker Compose support** + +.. code-block:: shell-session + + $ ./mvnw spring-boot:run -pl verifier-application \ + -Dspring-boot.run.profiles=local + +Spring Boot automatically starts the PostgreSQL container defined in +``compose.yaml`` before the application starts. + +**2. Verify the database container** + +.. code-block:: shell-session + + $ docker ps + CONTAINER ID IMAGE PORTS NAMES + abc123def456 postgres:15-alpine 0.0.0.0:5434->5432/tcp verifier_postgres + +The database is accessible on port 5434 to avoid conflicts with any +existing PostgreSQL installation. + + +Docker Compose full deployment +------------------------------ + +For testing the complete stack in containers, use the sample compose file +that includes both the verifier service and database: + +**1. Create a .env file with your credentials** + +.. code-block:: bash + + EXTERNAL_URL=https://your-verifier.example.com + VERIFIER_DID=did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:your-uuid + DID_VERIFICATION_METHOD=did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:your-uuid#auth-key-01 + SIGNING_KEY="-----BEGIN EC PRIVATE KEY----- + ...your key... + -----END EC PRIVATE KEY-----" + OPENID_CLIENT_METADATA_FILE=/verifier_metadata.json + +**2. Start the stack** + +.. code-block:: shell-session + + $ docker compose -f sample.compose.yml up -d + +This starts: + +- ``verifier_postgres``: PostgreSQL 15 database on internal port 5432 +- ``verifier-service``: The verifier application on port 8083 + +**3. Verify the deployment** + +.. code-block:: shell-session + + $ curl http://localhost:8083/swagger-ui/index.html + +The sample compose file: + +.. code-block:: yaml + + services: + verifier_postgres: + image: postgres:15-alpine + environment: + POSTGRES_USER: "verifier_user" + POSTGRES_PASSWORD: "secret" + POSTGRES_DB: "verifier_db" + ports: + - "5434:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U verifier_user -d verifier_db"] + interval: 5s + timeout: 5s + retries: 5 + + verifier-service: + image: ghcr.io/swiyu-admin-ch/swiyu-verifier:latest + configs: + - source: verifier_metadata + target: /verifier_metadata.json + ports: + - "8083:8080" + environment: + EXTERNAL_URL: ${EXTERNAL_URL} + OPENID_CLIENT_METADATA_FILE: ${OPENID_CLIENT_METADATA_FILE} + VERIFIER_DID: ${VERIFIER_DID} + DID_VERIFICATION_METHOD: ${DID_VERIFICATION_METHOD} + SIGNING_KEY: ${SIGNING_KEY} + POSTGRES_USER: "verifier_user" + POSTGRES_PASSWORD: "secret" + POSTGRES_JDBC: "jdbc:postgresql://verifier_postgres:5432/verifier_db" + + configs: + verifier_metadata: + content: | + { + "client_id": "${VERIFIER_DID}", + "client_name": "My Verifier", + ... + } + + +Debug mode +---------- + +To run the verifier with remote debugging enabled: + +.. code-block:: shell-session + + $ ./mvnw spring-boot:run -pl verifier-application \ + -Dspring-boot.run.profiles=local-dockerless \ + -Dspring-boot.run.fork=true \ + -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" + +This starts the JVM debug agent on port 5005. Connect your IDE's remote +debugger to ``localhost:5005``. + + +Identity configuration +---------------------- + +These settings establish your verifier's identity within the Swiyu +Trust Infrastructure. All values are derived from the DID generation +process during onboarding. + +``application.client_id`` + Your verifier's DID as registered on the Base Registry. This uniquely + identifies your service to wallets and other participants. + Example: ``did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:uuid`` + +``application.client_id_scheme`` + The client identifier scheme. Set to ``did`` when using Decentralized + Identifiers. + +``application.signing-key-verification-method`` + The full DID with key fragment that identifies the public key in your + DID Document. This tells wallets where to find the key for verifying + your signed request objects. + Example: ``did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:uuid#auth-key-01`` + +``application.signing_key`` + The private EC key used to sign OID4VP request objects. This must be + the private key corresponding to the public key published in your DID + Document. Include the full PEM content including BEGIN and END markers:: + + application: + signing_key: | + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEI...your_key_content... + -----END EC PRIVATE KEY----- + +``application.external-url`` + The publicly accessible URL of your verifier service. Wallets use this + URL to retrieve request objects and submit presentations. Must be HTTPS + in production. + Example: ``https://verifier.example.com`` + +``application.client-metadata-file`` + Path to the verifier metadata JSON file. This file contains display + information shown to users in their wallet, including localized names + and logo. + Example: ``classpath:/client_metadata.json`` + + +Database configuration +---------------------- + +Configure the PostgreSQL connection for storing verification state: + +``spring.datasource.url`` + JDBC connection URL for the PostgreSQL database. + Example: ``jdbc:postgresql://localhost:5432/verifier_db`` + +``spring.datasource.username`` + Database username for authentication. + +``spring.datasource.password`` + Database password for authentication. + +``spring.jpa.hibernate.ddl-auto`` + Schema management strategy. Use ``create`` for initial setup (creates + schema from entity definitions), ``update`` for adding new fields to + existing tables, or ``validate`` for production (verifies schema matches + entities without modifications). + + +Operational parameters +---------------------- + +These settings control verification behavior and resource management: + +``VERIFICATION_TTL_SEC`` + How long a verification request remains valid, in seconds. After this + period, the verification expires and the user must start a new request. + Default: ``900`` (15 minutes) + +``DATA_CLEAR_PROCESS_INTERVAL_MS`` + Interval between cleanup runs that remove expired verifications from + the database, in milliseconds. + Default: ``420000`` (7 minutes) + +``STATUS_LIST_CACHE_TTL_MILLI`` + How long to cache credential status list results, in milliseconds. + Caching reduces load on the Status Registry. + Default: ``900000`` (15 minutes) + +``ISSUER_PUBLIC_KEY_CACHE_TTL_MILLI`` + How long to cache issuer public key lookups, in milliseconds. + Caching reduces load on the Base Registry. + Default: ``3600000`` (1 hour) + + +Webhook configuration +--------------------- + +Configure webhook callbacks to receive notifications when verification +status changes, instead of polling: + +``webhook.callback-uri`` + Full URL where webhook notifications should be sent. If not set, + webhooks are disabled and clients must poll for status updates. + Example: ``https://kych.example.com/notification`` + +``webhook.callback-interval`` + How often to send queued webhook notifications, in milliseconds. + Failed deliveries are retried on subsequent intervals. + Default: ``5000`` (5 seconds) + +``WEBHOOK_API_KEY_HEADER`` + HTTP header name for API key authentication on the webhook endpoint. + Optional but recommended for production. + Example: ``X-API-Key`` + +``WEBHOOK_API_KEY_VALUE`` + The API key value to include in webhook requests. Required if + ``WEBHOOK_API_KEY_HEADER`` is set. + +Webhook payloads contain: + +.. code-block:: json + + { + "verification_id": "uuid-of-verification", + "timestamp": "2025-01-23T10:30:00Z" + } + + +Security configuration +---------------------- + +The Management API can be secured using OAuth 2.0 resource server +authentication via Spring Security. + +For fixed public key authentication: + +``SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_PUBLICKEYLOCATION`` + URI path to a public key in PEM format for JWT verification. + Example: ``file:/etc/kych/management-api-public-key.pem`` + +For dynamic key discovery via authorization server: + +``SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUERURI`` + URI to the OAuth 2.0 authorization server. The verifier fetches + the public key from the server's ``/.well-known/openid-configuration``. + +``SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWKSETURI`` + Direct URI to the JWK Set endpoint, bypassing OpenID Connect discovery. + +``SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWSALGORITHMS`` + Comma-separated list of accepted JWT signature algorithms. + Default: ``RS256`` + + +Example configuration +--------------------- + +Complete configuration example for production deployment: + +.. code-block:: yaml + + application: + external-url: "https://verifier.example.com" + client_id: "did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:abc123" + client_id_scheme: "did" + signing-key-verification-method: "did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:abc123#auth-key-01" + signing_key: | + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEIABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu + oAoGCCqGSM49AwEHoUQDQgAE5cice... + -----END EC PRIVATE KEY----- + client-metadata-file: "classpath:/client_metadata.json" + + spring: + datasource: + driver-class-name: org.postgresql.Driver + url: "jdbc:postgresql://localhost:5432/verifier_db" + username: "verifier_user" + password: "secure_password_here" + jpa: + hibernate: + ddl-auto: validate + + webhook: + callback-uri: "https://kych.example.com/notification" + callback-interval: 5000 + + +Running the service +=================== + +See the `Deployment Options`_ section for detailed instructions on running +the verifier in different environments. This section covers verification +and additional runtime configuration. + + +Verifying the deployment +------------------------ + +After starting, verify the service is running by accessing the Swagger UI: + +``https://<your-external-url>/swagger-ui/index.html`` + +The Management API endpoints are available under ``/management/api/`` +and the OID4VP endpoints under ``/oid4vp/api/``. + + +Client metadata configuration +----------------------------- + +The ``client_metadata.json`` file defines how your verifier appears to +users in their wallet. Create this file with localized display information: + +.. code-block:: json + + { + "client_id": "${VERIFIER_DID}", + "client_name": "My Verifier Service", + "client_name#en": "My Verifier Service", + "client_name#de": "Mein Verifizierdienst", + "client_name#fr": "Mon service de verification", + "logo_uri": "https://example.com/logo.png", + "vp_formats": { + "jwt_vp": { + "alg": ["ES256"] + } + } + } + +The ``vp_formats`` field specifies which Verifiable Presentation formats +your verifier accepts. For SD-JWT credentials, ``ES256`` is the standard +algorithm. + + +Integration with KyCH OAuth2 Gateway +==================================== + +The Swiyu Verifier integrates with KyCH OAuth2 Gateway to provide KYC +verification for GNU Taler exchanges. This section describes the +required configuration on both sides. + + +KyCH client configuration +------------------------- + +In the KyCH configuration file, configure each client with the Swiyu +Verifier URL and webhook endpoint: + +.. code-block:: ini + + [client_exchange] + CLIENT_ID = taler-exchange + CLIENT_SECRET = secure_secret + VERIFIER_URL = https://verifier.example.com + VERIFIER_MANAGEMENT_API_PATH = /management/api/verifications + REDIRECT_URI = https://exchange.example.com/kyc/callback + ACCEPTED_ISSUER_DIDS = {did:tdw:trusted_issuer_did} + + +Webhook configuration +--------------------- + +Configure the Swiyu Verifier to send status notifications to KyCH's +notification endpoint: + +.. code-block:: yaml + + webhook: + callback-uri: "https://kych.example.com/notification" + callback-interval: 5000 + +This allows KyCH to receive immediate notification when a verification +completes, rather than relying on polling. + + +Verification flow with KyCH +--------------------------- + +When integrated with KyCH, the verification flow is: + +1. KyCH receives an OAuth 2.0 authorization request from the exchange. + +2. KyCH calls ``POST /management/api/verifications`` on the Swiyu Verifier + with the credential claims specified in the OAuth scope. + +3. The verifier returns a verification URI and deeplink. + +4. KyCH displays a QR code to the user or provides the deeplink. + +5. The user's Swiyu Wallet scans the QR code and presents the credential. + +6. The Swiyu Verifier validates the credential and sends a webhook to KyCH. + +7. KyCH retrieves the verified claims and continues the OAuth flow. + + +API Usage +========= + +This section describes how to use the Swiyu Verifier APIs directly, +which is useful for understanding the integration or for custom +implementations. + + +Creating a verification request +------------------------------- + +Create a verification request using the Management API: + +.. code-block:: console + + $ curl -X POST \ + -H "Content-Type: application/json" \ + -d @request.json \ + https://verifier.example.com/management/api/verifications + +Where ``request.json`` contains: + +.. code-block:: json + + { + "accepted_issuer_dids": ["did:tdw:issuer_did"], + "presentation_definition": { + "id": "00000000-0000-0000-0000-000000000000", + "input_descriptors": [{ + "id": "11111111-1111-1111-1111-111111111111", + "format": { + "vc+sd-jwt": { + "sd-jwt_alg_values": ["ES256"], + "kb-jwt_alg_values": ["ES256"] + } + }, + "constraints": { + "fields": [ + {"path": ["$.vct"], "filter": {"type": "string", "const": "betaid-sdjwt"}}, + {"path": ["$.age_over_18"]} + ] + } + }] + } + } + + +Request parameters +^^^^^^^^^^^^^^^^^^ + +``accepted_issuer_dids`` + List of trusted issuer DIDs. Only credentials from these issuers are + accepted. If omitted, credentials from any issuer are accepted (not + recommended for production). + +``trust_anchors`` + Alternative to ``accepted_issuer_dids``. List of trust registry entry + DIDs. The verifier accepts credentials from any issuer trusted by + these anchors. + +``presentation_definition`` + DIF Presentation Exchange format specifying required credentials and + claims. See the Presentation Definition section below. + +``jwt_secured_authorization_request`` + Whether to sign the request object. Default: ``true`` (recommended). + +``response_mode`` + How the wallet sends the response. Options: + + - ``direct_post``: Unencrypted response (current default) + - ``direct_post.jwt``: JWE-encrypted response (recommended for privacy) + + +Response +^^^^^^^^ + +.. code-block:: json + + { + "id": "verification-uuid", + "request_nonce": "random-nonce", + "state": "PENDING", + "verification_url": "https://verifier.example.com/oid4vp/api/request-object/request-id", + "verification_deeplink": "swiyu-verify://?client_id=..." + } + +Store the ``id`` for polling status. Use ``verification_deeplink`` to +generate a QR code for the user to scan. + + +Polling verification status +--------------------------- + +Check the status of a verification: + +.. code-block:: shell-session + + $ curl -X GET \ + https://verifier.example.com/management/api/verifications/{verification_id} + +The response includes the current ``state``: + +- ``PENDING``: Waiting for user to present credentials +- ``SUCCESS``: Verification completed successfully +- ``FAILED``: Verification failed (see ``error_code``) +- ``REJECTED``: User rejected the verification request +- ``EXPIRED``: Verification request timed out + + +Presentation definition format +------------------------------ + +The ``presentation_definition`` follows the DIF Presentation Exchange +specification. Each ``input_descriptor`` specifies one credential requirement: + +``format`` + Accepted credential formats. For Swiss Beta ID, use:: + + "vc+sd-jwt": { + "sd-jwt_alg_values": ["ES256"], + "kb-jwt_alg_values": ["ES256"] + } + +``constraints.fields`` + List of claim requirements. Each field specifies: + + - ``path``: JSONPath to the claim (e.g., ``["$.age_over_18"]``) + - ``filter``: Optional filter for exact value matching + +The first field should filter by credential type (``$.vct``). Subsequent +fields specify the claims to disclose. + + +Error codes +=========== + +When verification fails, the response includes an error code explaining +the failure: + +``credential_invalid`` + Generic credential validation failure. + +``credential_expired`` + The presented credential has expired. + +``credential_revoked`` + The credential has been revoked by the issuer. + +``credential_suspended`` + The credential is temporarily suspended. + +``credential_missing_data`` + The credential does not contain the requested claims. + +``issuer_not_accepted`` + The credential issuer is not in the accepted issuers list. + +``public_key_of_issuer_unresolvable`` + Could not retrieve the issuer's public key from the Base Registry. + +``unresolvable_status_list`` + Could not check credential revocation status. + +``holder_binding_mismatch`` + The holder could not prove control of the credential. + +``client_rejected`` + The user rejected the verification request in their wallet. + +``jwt_expired`` + A JWT in the verification process has expired. + +``invalid_format`` + The credential or presentation format is invalid. + + +Deployment considerations +========================= + +Production security +------------------- + +For production deployments: + +1. **Protect the Management API**: Use network segmentation or OAuth 2.0 + authentication to restrict access to internal systems only. + +2. **Use an API Gateway**: Place a WAF or API gateway in front of the + OID4VP endpoints to filter malicious requests. + +3. **Enable HTTPS**: The OID4VP endpoints must use HTTPS with a valid + certificate from a trusted CA. + +4. **Secure credentials**: Store the signing key securely. Consider using + an HSM for production key management. + +5. **Set accepted issuers**: Always specify ``accepted_issuer_dids`` or + ``trust_anchors`` in verification requests. + + +HSM support +----------- + +For production deployments, the signing key can be stored in a Hardware +Security Module (HSM) instead of the configuration file. Configure: + +``SIGNING_KEY_MANAGEMENT_METHOD`` + Set to ``pkcs11`` for Sun PKCS#11 provider or a vendor-specific value. + +``HSM_HOST``, ``HSM_PORT``, ``HSM_USER``, ``HSM_PASSWORD`` + Connection parameters for the HSM. + +``HSM_KEY_ID`` + Key identifier or alias within the HSM. + +See the Swiyu Verifier documentation for vendor-specific HSM configuration. + + +Monitoring +---------- + +The verifier exposes Prometheus metrics at ``/actuator/prometheus``. +Enable authentication for this endpoint in production: + +.. code-block:: yaml + + MONITORING_BASIC_AUTH_ENABLED: true + MONITORING_BASIC_AUTH_USERNAME: prometheus + MONITORING_BASIC_AUTH_PASSWORD: secure_password + + +See also +======== + +- kych.conf(5) - KyCH OAuth2 Gateway configuration +- kych-oauth2-gateway(1) - KyCH OAuth2 Gateway server +- `Swiyu Trust Infrastructure <https://swiyu-admin-ch.github.io/>`_ +- `OID4VP Specification <https://openid.net/specs/openid-4-verifiable-presentations-1_0.html>`_ +- `DIF Presentation Exchange <https://identity.foundation/presentation-exchange/>`_ + + +Bugs +==== + +Report bugs by using https://bugs.taler.net/ or by sending electronic +mail to <taler@gnu.org>.