Skip to content

Repository files navigation

authentik-alipay-kyc

Go + Vue middleware for launching Alipay real-name verification from an authentik application and writing the result back to authentik user attributes.

Flow:

  1. User clicks the 实名认证 application in authentik.
  2. This service authenticates the user through authentik OIDC.
  3. The user enters name and ID card number, then the service initializes Alipay identity verification and redirects the browser to Alipay.
  4. After Alipay returns, this service calls Alipay query API and only writes back when passed == "T".
  5. 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.

authentik Setup

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": "*三"
    }
  }
}

Alipay Setup

The service uses Alipay OpenAPI methods:

  • alipay.user.certify.open.initialize
  • alipay.user.certify.open.certify
  • alipay.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 ID Verification Setup

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.

Configuration

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.

Run

npm ci
npm run build
go test ./...
go run ./cmd/alipay-kyc

Docker:

docker compose up --build

docker-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.pem

Mount 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/stats

Example response:

{
  "total": 12,
  "success": 10,
  "failure": 2,
  "updated_at": "2026-06-22T09:00:00Z"
}

GHCR

.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.

References

About

Authentik <-> Alipay KYC 的中间件

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages