Go + Vue middleware for launching Alipay real-name verification from an authentik application and writing the result back to authentik user attributes.
Flow:
- User clicks the
实名认证application in authentik. - This service authenticates the user through authentik OIDC.
- The user enters name and ID card number, then the service initializes Alipay identity verification and redirects the browser to Alipay.
- After Alipay returns, this service calls Alipay query API and only writes back when
passed == "T". - authentik receives a user attribute containing verification status data.
authentik receives only an HMAC-SHA256 ID hash, ID last four characters, masked name with only the last character visible, channel, and verification time.
Verification counters are stored in a local JSON file and are not shown in the frontend. They can be read through an authenticated API.
The full submitted name and ID number are stored locally in encrypted per-person JSON files named exactly with the same id_hash value written to authentik. Each record uses AES-256-GCM for the PII payload and encrypts the data key with a configured RSA or SM2 public key. The private key is not required by the service and should be kept offline.
Alipay verification stays pending for 30 minutes by default. Users can retry result checks from the browser, and the server also polls pending Alipay certifications on a one-minute interval. Mobile browsers try to open the Alipay app through the external H5 launch URL while keeping the QR code as a fallback.
Set KYC_QR_NOTICE_HTML to show custom trusted HTML between the Alipay QR code and the result-check button. It is empty by default.
Create an OAuth2/OpenID Provider and an Application named 实名认证.
Provider settings:
- Redirect URI:
https://<kyc-service>/auth/callback - Launch URL:
https://<kyc-service>/ - Scopes:
openid profile email
Add a property mapping that exposes the authentik user primary key:
return {
"ak_user_id": str(request.user.pk),
}Create an authentik API token with permission to view and change users. Set AUTHENTIK_USER_ID_CLAIM=ak_user_id unless you use a different claim name.
The service PATCHes:
{
"attributes": {
"alipay_kyc": {
"verified": true,
"verified_at": "2026-06-21T09:00:00Z",
"channel": "alipay",
"id_hash": "hmac-sha256-hex",
"id_last4": "1234",
"name_masked": "*三"
}
}
}The service uses Alipay OpenAPI methods:
alipay.user.certify.open.initializealipay.user.certify.open.certifyalipay.user.certify.open.query
Configure the Alipay application for identity verification and set the return URL to https://<kyc-service>/verify/callback. The service also sends a notify URL at https://<kyc-service>/api/alipay/notify, but the browser return path performs the authoritative query and authentik write-back.
Aliyun Financial-grade ID Verification can be enabled as a manual fallback channel. When both Alipay and Aliyun are enabled, the user page starts with Alipay and shows a switch link for Aliyun. If only one provider is enabled, that provider is used directly. The frontend obtains MetaInfo from the Aliyun Web/H5 script for every start request, the server calls InitFaceVerify, and result confirmation uses DescribeFaceVerify with ResultObject.Passed == "T".
Enable it with ALIYUN_KYC_ENABLED=true, configure an AccessKey with permission to call CloudAuth APIs, and set ALIYUN_SCENE_ID to your financial-grade real-person verification scene ID. The default endpoints try Shanghai first and Beijing second:
cloudauth.cn-shanghai.aliyuncs.com,cloudauth.cn-beijing.aliyuncs.com
Aliyun CertifyId and CertifyUrl are valid for 30 minutes and can only be submitted once. Alipay pending verification defaults to 23 hours.
Set ALIYUN_ID2_META_VERIFY_ENABLED=true to enable Aliyun identity two-factor information verification. This flag can enable the admin information verification button independently of the Aliyun KYC channel, but it still requires Aliyun AccessKey credentials. When Aliyun KYC is also enabled, Aliyun KYC start requests run Id2MetaVerify before InitFaceVerify; only a passed result generates the Aliyun verification URL. This user-facing precheck is throttled per user and client IP with a short cooldown and daily cap to reduce accidental abuse.
| Variable | Required | Default | Description |
|---|---|---|---|
HTTP_ADDR |
no | :8080 |
Listen address. |
PUBLIC_URL |
yes | empty | Public base URL of this service. |
SESSION_KEYS |
production | generated | Comma-separated base64 keys, at least 32 bytes each. Generate with openssl rand -base64 64. |
SESSION_SECURE |
no | derived | Secure cookie flag. Defaults to true for HTTPS PUBLIC_URL. |
HASH_PEPPER |
yes | empty | Secret HMAC key for ID hashes. |
STATS_FILE |
no | /data/stats.json |
Local JSON file storing total, success, and failure counters. |
STATS_API_TOKEN |
yes | empty | Bearer token required for GET /api/stats. |
KYC_PII_DIR |
no | /data/kyc_pii |
Local directory for encrypted submitted name and ID number records. Each file is named <id_hash>. |
KYC_QR_NOTICE_HTML |
no | empty | Trusted HTML rendered between the Alipay QR code and the 我已完成,检查结果 button. |
PII_ENCRYPTION_PUBLIC_KEY_TYPE |
no | rsa |
Public key type for local PII encryption. Supported values: rsa, sm2. |
PII_ENCRYPTION_PUBLIC_KEY |
one of key or file | empty | PEM public key used to encrypt local PII records. RSA uses RSA-OAEP-SHA256; SM2 uses ASN.1 SM2 ciphertext. |
PII_ENCRYPTION_PUBLIC_KEY_FILE |
one of key or file | empty | Path to a PEM public key file. Use this instead of PII_ENCRYPTION_PUBLIC_KEY when mounting the key into Docker. |
ADMIN_ENABLED |
no | false |
Enable the manual import page at /admin/. |
ADMIN_ALLOWED_USERNAMES |
when admin enabled | empty | Comma-separated OIDC usernames allowed to use /admin/ after normal login. |
KYC_TIMEOUT_SECONDS |
no | 82800 |
Pending Alipay verification timeout. Defaults to 23 hours. Aliyun is always 30 minutes. |
KYC_POLL_INTERVAL_SECONDS |
no | 60 |
Server-side polling interval for pending Alipay verification. |
OIDC_ISSUER |
yes | empty | authentik provider issuer URL. Use the exact issuer from authentik discovery, usually ending with /, for example https://auth.example.com/application/o/alipay-kyc/. |
OIDC_CLIENT_ID |
yes | empty | OIDC client ID. |
OIDC_CLIENT_SECRET |
yes | empty | OIDC client secret. |
OIDC_REDIRECT_URL |
no | ${PUBLIC_URL}/auth/callback |
OIDC callback URL. |
AUTHENTIK_BASE_URL |
yes | empty | authentik base URL. |
AUTHENTIK_TOKEN |
yes | empty | authentik API token. |
AUTHENTIK_USER_ID_CLAIM |
no | ak_user_id |
OIDC claim used as authentik user pk. |
AUTHENTIK_ATTRIBUTE_KEY |
no | alipay_kyc |
User attribute key written by the service. |
AUTHENTIK_VERIFIED_GROUP_UUID |
no | empty | Optional authentik group UUID. Verified users are added to this group after KYC, and admins can manually sync already verified users into it. |
ALIPAY_KYC_ENABLED |
no | true |
Enable Alipay KYC. If both Alipay and Aliyun are enabled, Alipay is the default provider. |
ALIPAY_GATEWAY_URL |
no | https://openapi.alipay.com/gateway.do |
Alipay OpenAPI gateway. |
ALIPAY_APP_ID |
when Alipay enabled | empty | Alipay app ID. |
ALIPAY_APP_PRIVATE_KEY |
when Alipay enabled | empty | RSA private key PEM, \n escapes are accepted. |
ALIPAY_PUBLIC_KEY |
when Alipay enabled | empty | Alipay public key PEM. |
ALIPAY_BIZ_CODE |
no | FACE |
Alipay identity verification scene code. |
ALIPAY_CERT_TYPE |
no | IDENTITY_CARD |
Alipay certificate type. |
ALIPAY_RETURN_URL |
no | ${PUBLIC_URL}/verify/callback |
Browser return URL. |
ALIPAY_CALLBACK_URL |
no | ${PUBLIC_URL}/api/alipay/notify |
Alipay notify URL. |
ALIYUN_KYC_ENABLED |
no | false |
Enable Aliyun Financial-grade ID Verification as a fallback channel. |
ALIYUN_ACCESS_KEY_ID |
when Aliyun enabled | empty | Alibaba Cloud AccessKey ID for CloudAuth. |
ALIYUN_ACCESS_KEY_SECRET |
when Aliyun enabled | empty | Alibaba Cloud AccessKey secret for CloudAuth. |
ALIYUN_SCENE_ID |
when Aliyun enabled | empty | Aliyun ID Verification scene ID. |
ALIYUN_ENDPOINTS |
no | cloudauth.cn-shanghai.aliyuncs.com,cloudauth.cn-beijing.aliyuncs.com |
Comma-separated CloudAuth endpoints tried in order. |
ALIYUN_PRODUCT_CODE |
no | ID_PRO |
Aliyun product code. |
ALIYUN_MODEL |
no | MOVE_ACTION |
Aliyun liveness model. |
ALIYUN_CERT_TYPE |
no | IDENTITY_CARD |
Aliyun certificate type. |
ALIYUN_RETURN_URL |
no | ${PUBLIC_URL}/verify/callback |
Aliyun browser return URL. |
ALIYUN_TIMEOUT_SECONDS |
no | 10 |
Aliyun API client timeout. |
ALIYUN_ID2_META_VERIFY_ENABLED |
no | false |
Enable Aliyun Id2MetaVerify. This also shows the admin identity two-factor button and enables the user-facing Aliyun precheck before QR generation. |
ALIYUN_MOBILE3_META_DETAIL_VERIFY_ENABLED |
no | false |
Enable the admin-only Aliyun Mobile3MetaDetailVerify detailed phone three-factor button. |
npm ci
npm run build
go test ./...
go run ./cmd/alipay-kycDocker:
docker compose up --builddocker-compose.yml uses a named volume for /data so the local stats file survives container rebuilds. If you replace it with a bind mount such as ./data:/data, make sure the directory is writable by container UID 65532.
Generate an RSA key pair for local PII encryption:
openssl genrsa -out pii-private.pem 3072
openssl rsa -in pii-private.pem -pubout -out pii-public.pemMount pii-public.pem into the container and set PII_ENCRYPTION_PUBLIC_KEY_FILE=/pii-public.pem, or set PII_ENCRYPTION_PUBLIC_KEY to the public key PEM text. Keep pii-private.pem outside the service host unless you need offline decryption.
For SM2 public keys, set PII_ENCRYPTION_PUBLIC_KEY_TYPE=sm2 and provide the SM2 public key as a PEM PUBLIC KEY.
Decrypt a local PII record offline:
scripts/decrypt-pii-rsa.sh ./pii-private.pem ./alipay-kyc-data/kyc_pii/<id_hash>
scripts/decrypt-pii-sm2.sh ./pii-private.pem ./alipay-kyc-data/kyc_pii/<id_hash>Manual admin import:
Set ADMIN_ENABLED=true and ADMIN_ALLOWED_USERNAMES to enable /admin/ for specific logged-in users. Authorization is based on the OIDC username stored in the session, typically preferred_username. The admin page can import a user by authentik user ID, name, ID number, and a 需要 KYC 认证 switch.
When 需要 KYC 认证 is 否, the service writes the same local encrypted PII record and authentik attribute shape as the normal flow immediately, using channel: "admin", without incrementing verification counters.
When 需要 KYC 认证 is 是, the service creates a 24-hour shortcut KYC link and QR code for the administrator. The user can open that link without logging in and verify against the administrator-provided identity information, or log in normally and use the prefilled identity prompt shown on the standard KYC page. The shortcut identity cannot be edited by the user; if it is wrong, the administrator should generate a new shortcut link. Once a provider certification URL is generated, that pending verification keeps the identity information used at generation time, so later edits or a new start do not change the old pending result.
If AUTHENTIK_VERIFIED_GROUP_UUID is set, every successful KYC writeback also adds the authentik user to that group. The admin page also shows a 同步已认证用户到组 button, which scans users with the configured KYC attribute marked verified: true and adds them to the configured group.
When the Aliyun information verification flags are enabled, the admin page shows 身份二要素核验 and/or 手机号三要素核验详版 buttons. These buttons require the normal admin login, allowed username, and CSRF token, and ask for confirmation before calling Aliyun to reduce accidental requests. They are not tied to the user-facing precheck limiter. The detailed phone three-factor button reads the target user's confirmed authentik SMS Device phone number server-side; if the user has no SMS Device or the number cannot be normalized to an 11-digit mainland China mobile number, the button stays disabled and the server refuses the Aliyun request.
Stats API:
curl -H "Authorization: Bearer $STATS_API_TOKEN" \
https://<kyc-service>/api/statsExample response:
{
"total": 12,
"success": 10,
"failure": 2,
"updated_at": "2026-06-22T09:00:00Z"
}.github/workflows/docker.yml builds and tests the app, then publishes multi-arch images to ghcr.io/<owner>/<repo> on branch and tag pushes. Pull requests build without pushing.
- Authentik API:
PATCH /api/v3/core/users/{id}/for updating userattributes: https://api.goauthentik.io/reference/core-users-partial-update/ - Alipay identity verification product/API documentation: https://opendocs.alipay.com/open/009yj1?pathHash=6cff73be